【问题标题】:Assigning system command's output to variable将系统命令的输出分配给变量
【发布时间】:2010-12-29 23:22:52
【问题描述】:

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

例子:

$ date | awk --field-separator=! {$1 = system("strip $1"); /*more processing*/}

应调用strip 系统命令,而不是将输出发送到shell,应将输出分配回$1 以进行更多处理。现在,它正在向 shell 发送输出并将命令的 retcode 分配给 $1

【问题讨论】:

  • nit:输出不会进入外壳,而是进入终端/控制台。 shell 不会读取其子级的任何输出——它们只是共享与同一 tty 关联的文件描述符。

标签: awk pipe


【解决方案1】:

注意:协进程是 GNU awk 特定的。 无论如何,另一种选择是使用 getline

cmd = "strip "$1
while ( ( cmd | getline result ) > 0 ) {
  print  result
} 
close(cmd)

调用close(cmd) 将防止awk 在多次调用后抛出此错误:

致命:无法打开管道`...'(打开的文件太多)

【讨论】:

  • 谢谢。这样,我可以从我的答案中删除 & 。看起来更酷。但我写的只是为了在 Linux 中使用,所以 gawk 不可用应该不是问题吗?
  • 是的,应该不是问题。您仍然应该检查文档并查看 coprocess 是否仅在某些版本的 gawk 中可用。我不记得在我的头顶
  • 从 3.1 版开始。红帽有 3.1.5。无论如何,我会使用你建议的方式,除非我想向命令的标准输入发送一些东西,在这种情况下,协同处理是有帮助的。
  • Awk 总是让我惊叹。
  • 请注意,如果您对上面的代码有一个 for 循环,那么 close(cmd) 是必要的,因为我发现 awk1018 迭代之后爆发的艰难方式(这可能取决于你的系统)
【解决方案2】:

要在awk 中运行系统命令,您可以使用system()cmd | getline

我更喜欢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)
     }'

请注意,如果您有多个结果,请务必使用 close() 以防止出现“打开的文件过多”错误(感谢 mateuscb 在 cmets 中指出这一点)。


使用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

【讨论】:

  • +1 用于添加close(),如果你不添加它,并且有多个结果,你可能会得到“打开的文件太多”。如果你有更长的命令,你可以做cmd = "date -j -f %s"; cmd | getline mydate; close(cmd)
  • @mateuscb 非常感谢您的反馈。我更新了问题以包含您有用的 cmets。
  • 感谢 close() 命令的提醒。它有很大帮助。如果不使用 close(),我有时会得到多个结果的错误日期结果。随着关闭()。我的多个日期结果都正确显示。
  • close(cmd) 在多次调用的awk 内部函数中执行cmd | getline var 时对我来说至关重要。第二次调用它并触发 getline 时,不再填充 var
  • close(cmd):有很大帮助。首先,它释放文件描述符。第二:它还“刷新”标准输出,从而使显示效果更好(但它确实也需要花费一些“时间”,为每个操作调用关闭。但是,应该支付“成本”)。
【解决方案3】:

想通了。

我们使用awk的Two-way I/O

{
  "strip $1" |& getline $1
}

将$1传递给strip,getline将输出从strip返回到$1

【讨论】:

【解决方案4】:
gawk '{dt=substr($4,2,11); gsub(/\//," ",dt); "date -d \""dt"\" +%s"|getline ts; print ts}'

【讨论】:

  • 如果你发布答案,你应该解释不同的部分(你做了什么以及它为什么起作用)。以便其他人可以从您的答案中学习。对于某些人来说,这条线是自我解释的。但对其他人来说,很难完全按照你的做法。
  • 注意:你应该使用 close(cmd) 和 getline,否则如果运行批量数据,结果是错误的。 More here
【解决方案5】:

当你需要处理一个 grep 输出时可以使用它:

echo "some/path/exex.c:some text" | awk -F: '{ "basename "$1"" |& getline $1; print $1 " ==> " $2}'

option -F: 告诉 awk 使用 : 作为字段分隔符

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

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

output:
exex.c ==> some text

【讨论】:

    【解决方案6】:

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

    如果非零退出状态则退出:

    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
    }
    

    所以基本上我只是将cmd 更改为在新行上打印命令的退出状态。执行上述while 循环后,res 将包含命令的退出状态和 value 将包含命令的完整输出。

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

    【讨论】:

    • 不错的技巧,将返回值添加为最后一行。但也许更简单:tmpfile="somename" ; cmd="thingyouwant &gt;" tmpfile ; res=system(cmd) ; close(cmd) 然后使用简单的getline 解析tmpfile 得到thingyouwant 的输出? (然后用另一个 cmd="rm " tmpfile 删除它(你的 system(cmd) 和 close(cmd) 也是如此)
    • 是的,这样干净多了。我建议您也为此添加一个新答案。我现在无法测试它的速度和正确性,但如果它适合我​​的代码,我会尝试使用这种方式。
    猜你喜欢
    • 2012-02-23
    • 2012-07-16
    • 2017-09-01
    • 1970-01-01
    • 2014-01-08
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    相关资源
    最近更新 更多