特定のキーワードで囲まれた複数のデータブロックを含むデータファイルがあります(DATA
、END
)。 awk
を使用して、ブロックから取得したファイル名に基づいて、データブロックを個別のファイルに抽出しています。一部のデータブロックは同じ名前を共有しているため、ファイル( “blockname
“)が既に存在する場合は、各出力ファイルの名前を整数で変更します。
#cat input.file useless stuff1 DATA blockname1 data1 data1 END useless stuff2 DATA blockname2 data2 data2 END useless stuff3 DATA blockname1 data3 data3 END useless stuff4
3つの出力ファイルblockname1
、blockname2
、および(最後のファイルに整数が割り当てられていることに注意してください)
#cat blockname1 DATA blockname1 data1 data1 END
(他のファイルはそれに応じて…)
これで、次のスクリプトが希望どおりに機能します。
awk "BEGIN { FS=" +" } ; \ /DATA/,/END/ \ { if ( $1 ~ /DATA/ ) \ { block=$2 ; i=0 ; file=block ;\ while ( system("test ! -e " file ) ) \ { i++ ; file=block"_"i ; print file } \ } ; \ print $0 > file \ } " \ input.file
問題はwhile
ループにあります。そのシステムコール:
system("test -e " file)
は、file
が存在する場合はTRUEであり、
はまだ存在しません。つまり、file
が存在している場合にのみ実行を開始するwhile
ループです。 (新しい)file
がまだ存在しない場合に中断します。
ただし、(そして、print file
で冗長にします)、整数の接尾辞が増加し、反対のsystem("test !-e " file)
は望ましい結果をもたらします。
したがって、これは私が期待したものとはまったく逆の動作をします。
回答
OK、私は考えました:問題はtest
の終了ステータスとwhile
awk
のループ条件。
正のtest
コマンドの結果ははTRUEで、負の値は1
でFALSEです。
ただし、awk
while
ループは0
をFALSEとして解釈し、1
をTRUEとして解釈するため、正反対です。定義。
例として:
awk "{ while ( 0 ) ; { print "0" } }" file
プロにはなりません
awk "{ while (1) ; { print "1" } }" file
は無限の1
を出力しますが、出力を生成します。
ベストプラクティスしたがって、このような組み合わせでは明示的にする必要があります
while ( system("command") == 0 )
または
while ( system("command") == 1 )
それぞれ。
私の場合
while ( system("test -e " file ) == 0 )
は予想される動作を示しています。
回答
awk
system()
は、実行したコマンドの終了ステータスを返します。成功した場合は0 、!成功しなかった場合は0。簡単な例として、次のコマンドを実行してみてください。
v = system("date");
vは0になります
実行した場合:
v = system("dat");
vが127であるか、値が0と異なる可能性があります。datコマンドがないか見つからない場合、OSからエラーが返されます。
回答
私があなたを理解している場合、目標はinput.fileのコンテンツをさまざまなファイルに抽出して、同じ名前のブロックが失われないようにすることです。
それが場合、そして、抽出前にターゲットディレクトリが常に空である場合は、より良い(そしてより速い)解決策があります:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
このように、awkは「ファイルが存在するかどうかをテストするためだけに、新しいシェルをN回実行する必要はありません。
注:
- awkのフィールドセパレーターであるため、BEGINブロックは必要ありません。はすでにスペースです。
- 一重引用符はすでに複数行であるため、行末に
"\"
は必要ありません。
while
状態の動作をよりよく理解していました。しかし、あなたの解決策はかなりきちんとしています-私のかさばるものよりも、ありがとう、私は好きです。 BEGINブロックは、私の異なるファイル形式の残り物です。一般的な例では役に立たなくなるのを忘れていました。バックスラッシュを省略すると、問題が発生しなくなります。これもありがとう。ただし、スクリプトの'の意図については注意が必要です。n = blocks[block]++; file=block (n? "_" n: "")
の部分についてもう少し詳しく説明していただけますか?blocks
という1つの配列を使用します。ブロック名で。例"blockname1"
の最初のインスタンス:blocks["blockname1"]
。 Awkはそのインデックスを見つけ、'が見つからないため、""
(これもゼロと見なされます)を想定しています。これで、awkではn = var++
は{n=var;var++}
と同等であるため、n==""
とblocks["blockname1"]==1
。最後に、file=block (n? "_" n: "")
は{file=block;if(n!="") file+="_" n}
と同じです。