将 awk 的 print/printf 输出通过管道传输到 shell 命令中,使得该语句在所有其他不相关的 print/printf 语句之后运行

将 awk 的 print/printf 输出通过管道传输到 shell 命令中,使得该语句在所有其他不相关的 print/printf 语句之后运行

鉴于这个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包含两行,foobar;该cat > file命令不会针对每一行单独运行。

答案2

awk 中的重定向和管道与 sh 中的重定向和管道类似,但有一个主要区别。在 sh 中,仅在命令执行期间foo >bar保持打开状态,并等待和终止。在 awk 中,重定向或管道保持打开状态,直到显式关闭为止,并且多次重定向或管道传输到同一文件名或命令会重用打开的重定向/管道。barfoofoo | barfoobar

例如,在 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内置命令进行重定向,但不能用于管道:必须将管道作为一个整体应用于(可能是复合)命令。

相关内容