将 gawk 的输入重定向到系统命令

Redirect input for gawk to a system command

通常 gawk 脚本会处理其标准输入的每一行。是否可以在脚本中指定一个系统命令,使用脚本其余部分中命令输出的每一行来处理?

例如考虑以下简单交互:

$ { echo "abc"; echo "def"; } | gawk '{print NR ":" [=10=]; }'
1:abc
2:def

我想在不使用管道的情况下获得相同的输出,而是将 echo 命令指定为系统命令。

我当然可以使用管道,但这会迫使我要么使用两个不同的脚本,要么在 bash 脚本中指定 gawk 脚本,我正在努力避免这种情况。

更新

前面的例子不太能代表我的用例,这个比较接近:

$ { echo "abc"; echo "def"; } | gawk '/d/ {print NR ":" [=11=]; }'
2:def

更新 2

一个shell并行脚本如下。如果没有 exec 行,脚本将从 stdin 读取;对于 exec,它将使用该行作为输入的命令:

/tmp> cat t.sh
#!/bin/bash

exec 0< <(echo abc; echo def)
while read l; do
  echo "line:" $l
done
/tmp> ./t.sh 
line: abc
line: def

我相信你要找的是getline

awk '{ while ( ("echo abc; echo def" | getline line) > 0){ print line} }' <<< ''
abc
def

调整第二个例子的答案:

awk '{ while ( ("echo abc; echo def" | getline line) > 0){ counter++; if ( line ~ /d/){print counter":"line} } }' <<< ''
2:def

让我们分解一下:

awk '{ 
       cmd = "echo abc; echo def"

       # line below will create a line variable containing the ouptut of cmd
       while ( ( cmd | getline line) > 0){ 

          # we need a counter because NR will not work for us
          counter++; 

          # if the line contais the letter d
          if ( line ~ /d/){ 
             print counter":"line
          } 
        } 
    }' <<< ''
    2:def

从您的所有评论来看,听起来您想要的是:

$ cat tst.awk
BEGIN {
    if ( ("mktemp" | getline file) > 0 ) {
        system("(echo abc; echo def) > " file)
        ARGV[ARGC++] = file
    }
    close("mktemp")
}

{ print FILENAME, NR, [=10=] }

END {
    if (file!="") {
        system("rm -f \"" file "\"")
    }
}

$ awk -f tst.awk
/tmp/tmp.ooAfgMNetB 1 abc
/tmp/tmp.ooAfgMNetB 2 def

但老实说,我不会这样做。您正在将 shell 擅长的(creating/destroying 文件和进程)与 awk 擅长的(处理文本)相结合。