鉴于这个awk
脚本:
END {
print "Y" | "cat"
print "X"
print "X"
}
# Output:
# X
# X
# Y
既然 Y 应该在其他语句之前运行,为什么不首先打印 Y 呢?
答案1
如果您希望cat
进程在 s 之前终止(并Y
打印 )X
,那么只需close("cat")
在 s 之后调用即可print "Y" | "cat"
。
其余的内容都在联机帮助页中进行了解释,您最好阅读一下。
既然 Y 应该在其他语句之前运行,为什么不首先打印 Y 呢?
不cat
应该在其他语句之前写入其输出并终止。它可能会在两次调用之前、之后或之间写入其输出print "X"
。
当您print ... | "command ..."
在 awk 中使用类似的东西时,command ..
它作为异步进程启动,其标准输入连接到管道(通过 popen("command ...", "w")
),并且该进程不一定会在您调用之前终止并写入其输出close("command ...")
(或者在 awk 终止时隐式完成)。
请参阅如下示例:
BEGIN {
print "foo" | "cat > file"
print "bar" | "cat > file"
}
结果将file
包含两行,foo
和bar
;该cat > file
命令不会针对每一行单独运行。
答案2
awk 中的重定向和管道与 sh 中的重定向和管道类似,但有一个主要区别。在 sh 中,仅在命令执行期间foo >bar
保持打开状态,并等待和终止。在 awk 中,重定向或管道保持打开状态,直到显式关闭为止,并且多次重定向或管道传输到同一文件名或命令会重用打开的重定向/管道。bar
foo
foo | bar
foo
bar
例如,在 sh 中,这将打印a
, b
, c
, a
, b
,因为每个排序命令仅获取两行输入:
{ echo b; echo a; } | sort
echo c
{ echo b; echo a; } | sort
但在 awk 中,这会打印c
, a
, a
, b
, b
(假设 awk 的输出是行缓冲的,否则c
可能会延迟),因为只有一个sort
命令,并且在它拥有所有输入数据之前不会打印任何内容,这种情况只会发生当管道的输入侧关闭时。
{ print "b"; print "a"; } | "sort";
print "c";
{ print "b"; print "a"; } | "sort";
要使管道命令终止,请close
显式调用该函数。 Awk 退出时会隐式关闭所有打开的管道和重定向。这将打印a
, b
, c
, a
, b
:
{ print "b"; print "a"; } | "sort"; close("sort");
print "c";
{ print "b"; print "a"; } | "sort"; close("sort");
同样,此 awk 片段创建一个两行文件,因为foo
第一行打开一次,并且在第二行运行时仍然打开:
print "hello" >"foo";
print "world" >"foo";
而此 sh 片段创建一个单行文件,因为第二行打开第一行创建的文件并在写入之前截断它world
:
echo hello >foo
echo world >foo
awk 如此设计的主要原因是每行的处理都有一个隐式循环。在 sh 中,如果您想在循环中处理行,通常会在循环周围编写重定向:
while read line; do
if condition "$line"; then
process line
fi
done >output
但在 awk 中,无法将重定向应用于隐式循环,因此您编写
condition($0) { process $0 >"output" }
awk 方式也更强大,因为您可以随意打开和关闭管道,即使是在循环或其他块的中间。在 sh 中,可以使用exec
内置命令进行重定向,但不能用于管道:必须将管道作为一个整体应用于(可能是复合)命令。