次の形式のテキストファイルがあります。
$data This is the experimental data
good data
This is good file
datafile
1 4324 3673 6.2e+11 7687 67576
2 3565 8768 8760 5780 8778 "This is line '2'"
3 7656 8793 -3e+11 7099 79909
4 8768 8965 8769 9879 0970
5 5878 9879 7.970e-1 9070 0709799
.
.
.
100000 3655 6868 97879 96879 69899
$.endfile
行「2」から「100000」までの3番目と4番目の列のデータを、それぞれ1列と99999行で構成される2つの異なるテキストファイルのデータに置き換えたいと思います。
awk
これを行うには、他のUNIXコマンドをどのように使用しますかsed
?列区切り記号は空白です。
他の2つのテキストファイルはそれぞれ99999行で構成され、フォーマットは次のとおりです。
12414
12421
36347
3.4e+3
-3.5e22
987983
.
.
.
87698
答え1
奇妙な方法:
awk '{if(FNR==NR){f2[FNR+1]=$1;}
else{
if(FNR==1){k++;}
if(k==1){f3[FNR+1]=$1}
else{if($1~/^[0-9]+/ && $1>1){$3=f2[$1];$4=f3[$1];}
print}
}}' file2 file3 file1
明確にするために、これはコメントスクリプトで書かれたものと同じです。
#!/usr/local/bin/gawk -f
{
## NR is the current line number, irrespective of
## which input file is being read. FNR is the line
## number of the current file. It is reset to 1 each
## time a new file is opened. Therefore, FNR will be
## equal to NR only while the 1st file is being read.
if(FNR==NR){
## If this is the 1st file, save its 1st field
## in the array f2. The key of the array is the
## line number of the current file plus one. This is
## because you want to start modifying from row '2' onwards.
## Therefore, presumably, you want the 1st row of file2 to
## be the value for row '2' of your data file..
f2[FNR+1]=$1;
}
## If this is not the 1st file
else{
## If this is the 1st line of the current file
if(FNR==1){
## Increase the value of the variable k by 1.
k++;
}
## If k is currently 1, this means that the above has only
## been run once so we are currently reading the 1nd file.
if(k==1){
## Save the 1st field of this file (file3 in your example)
## in the array f3. The key of the array is the
## line number of the current file plus one.
f3[FNR+1]=$1
}
## If k is not 1, we are reading the 3rd file. In this case,
## your actual data.
else{
## If the 1st field is a number and is greater than 1.
## In other words, if this is one of the lines you want
## to change.
if($1~/^[0-9]+/ && $1>1){
## Set the 3rd field to be the value saved in the array
## f2 for the value of $1.
$3=f2[$1];
## Set the 4th field to be the value saved in the array
## f3 for the value of $1.
$4=f3[$1];
}
## Print the current line. Since this is outside the
## previous if block, it will print all lines irrespective
## of whether they've been modified.
print;
}
}
}
パール方式:
perl -lane 'BEGIN{
open(A,"file2"); while(<A>){chomp; $f2{$.+1}=$_;}
open(B,"file3"); while(<B>){chomp; $f3{$.+1}=$_;}}
if($F[0]=~/^\d+$/ && $F[0]>1){$F[2]=$f2{$F[0]}; $F[3]=$f3{$F[0]}}
print "@F"' file1
説明する
-lane
:l
各入力行の末尾から末尾の改行を自動的に削除し(と同じ)、chomp
各ステートメントに改行を追加します。空のスペースの各入力行は自動的に配列に分割され、Perlはawkのように実行されますprint
。 「入力ファイルの各行に提供されているスクリプトを実行します。a
@F
n
-e
BEGIN{...}
:入力ファイルを読み込む前に実行されます。この例では、各追加ファイルを開き、その内容をハッシュに保存し%f2
ます%f3
。これはawk
基本的に上記で使用したものと同じ配列です。if($F[0]=~/^\d+$/ && $F[0]>1){...}
:もう一度言うが、これはawkスクリプトと同じ論理です。このフィールドは、各ファイルに対応するエントリに置き換えられます。print "@F"
:すべてのフィールドが印刷されます。
答え2
100%のソリューションを必要とするわけではないので、awk
(a)理解しやすく、(b)awk
メモリの制約を強調しないハイブリッドソリューションを提供します。
awk '
$1 == 2 { secondpart = 1 }
{ if (!secondpart) {
print > "top"
} else {
print $1, $2 > "left"
print $5, $6, $7, $8, $9 > "right"
}
}' a
(cat top; paste -d" " left b c right) > new_a
rm top left right
あるいは、一時ファイルの1つを削除し、1つのコマンドでスクリプトを短縮することもできます。
(awk '
$1 == 2 { secondpart = 1 }
{ if (!secondpart) {
print
} else {
print $1, $2 > "left"
print $5, $6, $7, $8, $9 > "right"
}
}' a; paste -d" " left b c right) > new_a
rm left right
これにより、出力行の末尾に余分な空白が発生し、行に a
9つ以上のフィールド(列)があるとファイルのデータが失われます。これが問題であれば簡単に解決できます。
答え3
{ { paste -d\ /dev/fd/[345] |
sed 's/ \( [^ ]*\)\(.*\)/\2\1/'
} 3<<FILE1 4<<FILE2 5<<FILE3
$(<file1 sed '1,/^1/w /dev/fd/2
/^2/,$!d;s/ [^ ]*//4;s// /3')
FILE1
$(<file2 tr -s \\n)
FILE2
$(<file3 tr -s \\n)
FILE3
} 2>&1
上記のコマンドシーケンスでは、かなり多くの入力/出力ジャグリングを実行しています。完了するのはとても簡単です。file[23]
実際に同じです。どちらも99,999行の3/4行のコピーです。残りのものfile1
- それはすべてです。本質的にファイルは上記の例とまったく同じですが、5行が6行と7行にコピーされ、一致しますfile[23]
。
デフォルトでは、各ファイルには独自のファイル記述子と独自の準備があります。file[23]
準備はほとんど必要ありません。tr
繰り返されるすべての\n
行文字を1つに圧縮すると、空行が消えます。
file1
もう少し得なさい。まず、aで始まる最初の行までのすべての行が1
記録されますstderr
。次に、出力から削除されるため、にのみ出力されます>&2
。sed
次に、3/4列を選択して空白に置き換えます。これは、元の位置に2つの連続した空白文字があることを意味します。
paste
すべてのファイル記述子を収集し、すべて一緒に貼り付け、スペースで区切ります。次に、sed
2つの空白文字の直後に空白ではなく、文字の最初のシーケンスをそれに続くすべての文字に置き換えます。
stderr
最後に、ファイル記述子がstdout
追加されますstdout
。結果は次のとおりです。
出力
$data This is the experimental data
good data
This is good file
datafile
1 4324 3673 6.2e+11 7687 67576
2 3565 8768 12414 12414 8778
3 7656 8793 12421 12421 79909
4 8768 8965 36347 36347 0970
5 5878 9879 3.4e+3 3.4e+3 0709799
6 5878 9879 -3.5e22 -3.5e22 0709799
7 5878 9879 987983 987983 0709799
. . .
. . .
. . .
100000 3655 6868 87698 87698 69899
$.endfile
答え4
配列がない別の奇妙な方法は少し汚れているので、後で整理しようとしています。
awk 'function get(file,L) {x=1
while ( (getline < file) > 0) {if(NR==x)y=$0;x++}
close(file)
return y
}
ARGV[1]==FILENAME{d=$0;a=get(ARGV[2],$0);b=get(ARGV[3],$0);$0=d;$2=a;$3=b;print
}' file file1 file2