次のコマンドの実行に問題があります -->
find . -type f -name 'out*' |
xargs awk 'BEGIN{print "Filename, Energy"}/TOTAL ENERGY/{print FILENAME, "," $4}' >> energy.csv
出力ファイルのすべてのディレクトリを調べてエネルギーを解析し、ヘッダー列を使用してEnergy.csvファイルに書き込もうとしています。
問題は、ファイルの途中にヘッダー列を複数回書き込む場合がありますが、必ずしもそうではないということです。私はこの動作を理解していません。
答え1
xargs
(またはfind
)は、ユーザーが指示したコマンドを呼び出して、一度に必要な数のファイル名を渡します。常にARG_MAX
オーバーランを引き起こすよりも少ない。
したがって、awkスクリプトは複数のバッチの入力ファイルで呼び出され、BEGIN
awkが呼び出されるたびにその部分が実行されます。実行を開始する前に、awkスクリプトの外部でヘッダー行を初期印刷することでこの問題を回避できますfind
。
したがって、次のようにします。
{
ofs=','
printf '%s%s%s\n' 'Filename' "$ofs" 'Energy' &&
find . -type f -name 'out*' |
xargs awk -v OFS="$ofs" '/TOTAL ENERGY/{print FILENAME, $4}'
} > energy.csv
または次のようになります(コマンド自体を呼び出すことができるため、出力を強力にパイプするfind
必要はありません)。xargs
{
ofs=','
printf '%s%s%s\n' 'Filename' "$ofs" 'Energy' &&
find . -type f -name 'out*' -exec \
awk -v OFS="$ofs" '/TOTAL ENERGY/{print FILENAME, $4}' {} +
} > energy.csv
また、awk部分をより慣用的にし、,
ヘッダのsの後と,
残りの出力のsの前の偽の空白を削除しました。
答え2
xargs
(cross-argsの場合)は、入力から単語を読み取り、それをコマンドに渡すcross
コマンドarg
です。
その入力は無限に長くすることができますが、コマンドに渡すことができる引数の数は制限されていますが、そうでなくても引数リストを完全に渡す必要があるため、連続した引数を渡したくありません。入力にワードストリームがある場合は、xargs
すべての読み取りを渡し、すべてメモリに保存し、入力の終わりに達した場合にのみコマンドを開始する必要があります(存在する場合)。
また、find
単語リスト(ここではファイルパス)は、デフォルトで予想される形式では生成されませんxargs
。それらを互いに接続するには標準がfind ... -print0 | xargs -r0 cmd...
必要ですfind ... -exec cmd... {} +
。
したがって、ファイルのリストが十分に大きい場合、通常はxargs
(あなたの場合)複数回実行され、cmd
そのステートメントは毎回実行されます。awk
awk
BEGIN
多くのGNUコマンド(wc
、、、sort
...)には最近のオプション(またはGNUまたはGNUの述語)がdu
追加され、ファイルのNUL区切り文字または標準入力(このような)を引数として扱うためにファイルのリストを取得します。制限を避け、リスト全体をメモリに保存せず、標準入力からファイルを読み取るとすぐにファイル処理を開始できることを意味します。--files0-from
-files0-from
find
--null --verbatim-files-from --files-from
tar
xargs -r0
例えば、
find . -name '*.txt' -type f -print0 | wc --files0-from - -w --total=always
ファイルが見つかったら、w
ファイルの注文数を印刷し、最後に1行を印刷します。これは同時に実行されないwhereよりはるかに優れており、whereは複数行を出力できます。.txt
find
total
find . -name '*.txt' -type f -exec wc -w --total=always {} +
find
wc
total
GNUにはawk
まだそのようなオプションはありませんが、次のように直接実装できます。
find . -type f -name 'out*' -print0 | sort -Vz |
gawk '
function inputfile( old_RS,ret) {
if (ARGC > 1) delete ARGV[ARGC - 1]
old_RS = RS
RS = "\0"
ret = getline ARGV[ARGC++] < "-"
RS = old_RS
if (ret <= 0) exit(-ret)
}
BEGIN {inputfile()}
ENDFILE{inputfile()}
# then your awk script
BEGIN{
OFS = ","
print "Filename", "Energy"
}
/TOTAL ENERGY/ {print FILENAME, $4}' >> energy.csv
awk
(この特別なケースでは、そのヘッダを次の外部に印刷する方がはるかに簡単です。エドが見せた)。
次のものと同じperl -lan
です。
find . -type f -name 'out*' -print0 | sort -Vz |
perl -lane '
sub nextfile {
local $/ = "\0";
my $file = <STDIN> or exit;
shift @ARGV;
push @ARGV, $file
}
BEGIN {nextfile}
BEGIN {$, = ","; print "Filename", "Energy"}
print $ARGV, $F[3] if /TOTAL ENERGY/;
nextfile if eof'