Awk 分配系统命令';s输出到变量

Awk 分配系统命令';s输出到变量,awk,pipe,Awk,Pipe,我想在awk脚本中运行system命令,并将其输出存储在变量中。我一直在尝试这样做,但是命令的输出总是转到shell,我无法捕获它。关于如何做到这一点有什么想法吗 例如: $ date | awk --field-separator=! {$1 = system("strip $1"); /*more processing*/} 应该调用strip系统命令,而不是将输出发送到shell,应该将输出分配回$1,以便进行更多处理。现在,它将输出发送到shell,并将命令的retcode分配给$1计

我想在awk脚本中运行
system
命令,并将其输出存储在变量中。我一直在尝试这样做,但是命令的输出总是转到shell,我无法捕获它。关于如何做到这一点有什么想法吗

例如:

$ date | awk --field-separator=! {$1 = system("strip $1"); /*more processing*/}
应该调用
strip
系统命令,而不是将输出发送到shell,应该将输出分配回
$1
,以便进行更多处理。现在,它将输出发送到shell,并将命令的retcode分配给
$1

计算出来

我们用awk的


将$1传递给strip,getline将strip的输出返回到$1。注意:协进程是特定于GNU awk的。 无论如何,另一种选择是使用getline

cmd = "strip "$1
while ( ( cmd | getline result ) > 0 ) {
  print  result
} 
close(cmd)
调用
close(cmd)
将阻止
awk
在多次调用后抛出此错误:

致命:无法打开管道“…”(打开的文件太多)


要在
awk
中运行系统命令,可以使用或

我更喜欢
cmd | getline
,因为它允许您将值捕获到变量中:

$ awk 'BEGIN {"date" |  getline mydate; close("date"); print "returns", mydate}'
returns Thu Jul 28 10:16:55 CEST 2016
awk 'BEGIN {
       cmd = "date -j -f %s"
       cmd | getline mydate
       close(cmd)
     }'
一般来说,您可以将命令设置为变量:

$ awk 'BEGIN {"date" |  getline mydate; close("date"); print "returns", mydate}'
returns Thu Jul 28 10:16:55 CEST 2016
awk 'BEGIN {
       cmd = "date -j -f %s"
       cmd | getline mydate
       close(cmd)
     }'
请注意,如果您有多个结果,请务必使用
close()
以防止出现“生成太多打开的文件”错误(感谢您在注释中指出这一点)


使用
system()
,命令输出将自动打印,您可以捕获的值是其返回代码:

$ awk 'BEGIN {d=system("date"); print "returns", d}'
Thu Jul 28 10:16:12 CEST 2016
returns 0
$ awk 'BEGIN {d=system("ls -l asdfasdfasd"); print "returns", d}'
ls: cannot access asdfasdfasd: No such file or directory
returns 2

当您需要处理grep输出时,可以使用此选项:

echo "some/path/exex.c:some text" | awk -F: '{ "basename "$1"" |& getline $1; print $1 " ==> " $2}'
选项
-F:
告诉awk使用
作为字段分隔符

“basename”$1”“
在第一个字段上执行shell命令
basename

|&getline$1
读取子流中上一个shell命令的输出

output:
exex.c ==> some text

我正在使用macOS的
awk
,我还需要命令的退出状态。因此,我扩展了@ghostdog74的解决方案以获得退出状态:

如果退出状态为非零,则退出:
cmd=
cmd=cmd“printf\”\n$?\“”
last_res=“”
value=“”
而((cmd | getline res)>0){
如果(值==“”){
值=最后一次
}否则{
值=值“\n”最后\u res
}
最后的_res=res
}
关闭(cmd)
#现在'res'具有命令的退出状态
#并且'value'具有命令的完整输出
如果(res!=0){
出口1
}否则{
打印值
}
所以基本上我只是更改了
cmd
,在新行上打印命令的退出状态。执行上述
while
循环后,
res
将包含命令的退出状态和
value
将包含命令的完整输出


老实说,这不是一个很好的方法,我自己也想知道是否有更好的方法。

谢谢。这样,我就可以从我的答案中删除&了。看起来很酷。但我写这篇文章只是为了在Linux中使用,所以gawk的不可用性不应该是一个问题?是的,不应该是一个问题。不过,您应该检查文档,看看协进程是否仅在gawk的特定版本中可用。我已经记不清3.1版的内容了。RedHat拥有3.1.5。无论如何,我将使用您建议的方式,除非我想向命令的stdin发送一些内容,在这种情况下,协进程是有用的。Awk从未停止让我感到惊讶。请注意,如果您在上面的代码上有一个for循环,那么
close(cmd)
是必要的,因为我发现
awk
1018
迭代后很难爆发(这可能取决于您的系统)。nit:输出不是到shell,而是到终端/控制台。shell不读取其子级的任何输出——它们只是共享与同一tty关联的文件描述符。如果您发布了答案,您应该解释不同的部分(您做了什么以及它为什么工作)。这样别人就可以从你的答案中学习。对一些人来说,这句话是自我解释的。但对于其他人来说,很难完全理解您所做的操作。警告:您应该将close(cmd)与getline一起使用,否则,如果对批量数据运行,结果将是错误的。如果您需要多次调用同一个命令,我们必须关闭该命令(),这不是awk,而是特定于gawk(gnu-awk):“使用gawk,可以打开到另一个进程的双向管道”+1以添加
close()
,如果您不添加它,并且有多个结果,您可能会得到“生成太多打开的文件”。如果您有更长的命令,您可以执行
cmd=“date-j-f%s”;cmd | getline mydate;关闭(cmd)
@mateuscb非常感谢您的反馈。我更新了问题以包含您的有用评论。感谢您提醒使用close()命令。这很有帮助。如果不放置close(),我有时会得到错误的日期结果。用放近()。我的多个日期结果都正确显示。
close(cmd)
对于我在
awk
内部函数中执行
cmd | getline var
非常重要,该函数被多次调用。第二次调用它时,触发了
getline
,不再填充
var
:帮助很大。首先,它释放文件描述符。第二:它还“刷新”标准输出,从而使显示效果更好(但每次操作调用close也需要一点“时间”。不过,应该支付“费用”)。很好的技巧,将返回值作为最后一行添加。但可能更简单:
tmpfile=“somename”;cmd=“thingyouwant>”tmpfile;res=系统(cmd);关闭(cmd)
,然后使用简单的getline解析tmpfile以获得所需内容的输出?(然后用另一个
cmd=“rm”tmpfile
(您也可以使用系统(cmd)和关闭(cmd))删除它)是的,这更干净。我建议您也添加一个新的答案。我现在无法测试它的速度和正确性,但如果适合我,我会尝试使用这种方法
cmd = <your command goes here>
cmd = cmd" ; printf \"\n$?\""

last_res = ""
value = ""        

while ( ( cmd | getline res ) > 0 ) {

    if (value == "") {
        value = last_res
    } else {
        value = value"\n"last_res
    }

    last_res = res
}

close(cmd)

# Now `res` has the exit status of the command
# and `value` has the complete output of command

if (res != 0) {
    exit 1
} else {
    print value
}