我在执行以下命令时遇到问题 -->
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 之前的虚假空白。
答案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
、du
...)最近添加了一个--files0-from
选项(或GNU或GNU 中-files0-from
的谓词),以便它们获取文件列表来处理文件或标准输入中的 NUL 分隔(就像这样做)作为参数,它避免了限制,避免将整个列表存储在内存中,并且意味着它们可以在从标准输入读取文件后立即开始处理文件。find
--null --verbatim-files-from --files-from
tar
xargs -r0
例如,
find . -name '*.txt' -type f -print0 | wc --files0-from - -w --total=always
一旦找到文件,就会打印文件w
中的订单数,并在最后打印一行,这比where好得多,并且不会同时运行,而且 where可以输出多行。.txt
find
total
find . -name '*.txt' -type f -exec wc -w --total=always {} +
find
wc
total
GNUawk
还没有这样的选项,但是您应该能够使用以下方法自己实现它:
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
(尽管在这种特殊情况下,在as之外打印该标头要简单得多埃德已经表明)。
与 的等效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'