有没有办法在 AWK 中执行命令替换,并能够使用$n
AWK 的表示法引用替换命令中的字段?
例如
find | awk '/txt$/ {nl = $(wc -l $NF); print nl}'
我希望上面的内容能够打印每个.txt
文件中的行数。相反,它实际上返回与以下内容相同的输出:
find | awk '/txt$/ {print}'
问题一:有没有办法在 awk 中执行命令替换?
问题2:为什么上面的第一个咒语默默地失败并且只是打印文件名?
请注意,上述内容仅作为示例提供。我不是问如何通过其他方式打印每个文件的行数。例如通过for f in $(find -iname \*.txt); do wc -l $f; done
问题具体是关于如何在 AWK 程序中利用命令替换。
答案1
首先,免责声明:请不要解析 的输出find
。 下面的代码仅用于说明如何将命令替换合并到 Awk 脚本中,以便命令可以对 Awk 的输入片段进行操作。
到实际上wc -l
对找到的每个文件进行行计数( ) find
(这是示例用例),只需使用:
find . -type f -name '*txt' -exec wc -l {} +
但是,要回答您提出的问题:
Q1
回答你的问题1:
Q1:有没有办法在 awk 中执行命令替换?
当然有一种方法,来自man awk
:
命令 | getline [var] 运行命令,将输出通过管道传输到 $0 或 var(如上所述)和 RT。
所以(看引用!!):
find . | awk '/txt$/{"wc -l <\"" $NF "\"|cut -f1" | getline(nl); print(nl)}'
请注意,构建的字符串以及因此执行的命令是
wc -l <file
以避免打印wc
.
好吧,我避免了该命令所需的文件“关闭”(对于几个文件来说是安全的,但技术上不正确)。你实际上需要做:
find . | awk '/txt$/{
comm="wc -l <\"" $NF "\" | cut -f1"
comm | getline nl;
close (comm);
print nl
}'
这也适用于较旧的 awk 版本。请记住避免使用
打印点,这会使代码失败,因为点是目录,而 wc 不能使用它。.
find .
或者,避免使用点值:
find . | awk '/txt$/ && $NF!="." { comm="wc -l <\"" $NF "\" | cut -f1"
comm | getline nl;
close (comm);
print nl
}'
您可以将其转换为单行代码,但我认为它看起来很丑陋。
Q2
至于你的第二个问题:
问题 2:为什么上面的第一个咒语会默默地失败,而只是打印文件名?
因为 awk 不能正确解析 shell 命令。它将命令理解为:
nl = $(wc -l $NF)
nl --> variable
$ --> pointer to a field
wc --> variable (that has zero value here)
- --> minus sign
l --> variable (that has a null string)
$ --> Pointer to a field
NF --> Last field
然后,l $NF
变成 null 和 the 的串联文本在 las 字段(文件名)内。此类文本作为数值变量的扩展是数值 0
对于 awk 来说,它变成:
nl = $( wc -l $NF)
nl = $ ( 0 - 0 )
这变成了$0
整行输入,(对于上面的简单查找)只是文件名。
因此,以上所有内容只会打印文件名(好吧,从技术上讲,是整行)。
答案2
使用"weak quotes"
而不是在脚本'strong quotes'
内进行子 shell 扩展awk
,但在您的示例中这样做并不是一个特别有价值的实现。它看起来也非常丑陋:
$ awk "END { print \"$(echo hello)\"} " < /dev/null
hello