【问题标题】:Having awk act upon results from a system command让 awk 根据系统命令的结果采取行动
【发布时间】:2019-07-27 21:33:50
【问题描述】:

我在这里找到了一些关于从 awk 调用外部命令并将结果存储在变量中的极好的帮助。我无法找到的是如何让 awk 对结果采取行动,就像对普通输入文本文件一样。

我使用 awk 解析一个小的 HTML 文件(正在运行的 Tahoe LAFS 节点的状态页面),以便找到列出的一些 IP 地址。在每个 IP 地址上,我对特定端口运行 nmap 扫描,以查看它是否打开(是的,这将成为自动 Tahoe LAFS 网格监视器)。使用 if 语句,我可以从 nmap 中挑选出包含端口状态(打开/过滤/关闭)作为其第二个字段(通常是“8098/TCP 打开未知”)的输出行。我想去掉字段 1 和 3 的行,只保留字段 2,但是,$2 当然是指我用作 awk 脚本输入的 HTML 文件中的字段。我尝试了一个用户定义的函数,它只返回了 $2,但它也引用了输入 HTML 文件中的字段。

有没有办法在 awk 脚本中引用内部创建的变量中的字段?类似于 awk 脚本中的嵌套 awk 命令?

【问题讨论】:

  • 发布您的输入数据、预期输出以及您自己尝试构建解决方案的尝试。
  • 您使用的是什么操作系统?这听起来可能是最好不要完全在 awk 脚本中完成的工作。
  • 我在 Ubuntu 上。我意识到这可能会在 awk 之外更好地完成,但我真的很喜欢 awk 的简单正则表达式匹配和解析能力。一个更简洁的解决方案可能是将我的 awk 解析器的输出写入文件并在 shell 脚本中调用 awk 脚本。但现在我开始了......
  • 不,这不是一个更简洁的解决方案,但它不是唯一的选择。如果您按照@anubhava 的要求发布一些示例输入和预期输出,我们可以开始为您提供帮助。

标签: awk


【解决方案1】:

使用getline“函数”。它以通常的方式将$0 设置为整个记录,并将$1 设置为$NF

$ awk '/test/ {
>     while (("ping -c 2 google.com") | getline > 0) {
>         printf("$1 = %s, $2 = %s\n", $1, $2);
>     }
> }'
abc
test
$1 = PING, $2 = google.com
$1 = 64, $2 = bytes
$1 = 64, $2 = bytes
$1 = , $2 = 
$1 = ---, $2 = google.com
$1 = 2, $2 = packets
$1 = round-trip, $2 = min/avg/max/stddev
xyz  
$ 

编辑:在(cmd | getline) 周围添加括号(没有它们对我有用,但我猜有些 awk 变体需要它?)。

编辑 2:显然“getline 周围的括号”来自GNU awk manuals 中提到的一个完全不同的问题:

根据 POSIX,'表达式 |如果表达式包含除“$”之外的无括号运算符,则 getline' 不明确——例如,“echo”“date”| getline' 是模棱两可的,因为连接运算符没有括号。你应该把它写成'("echo " "date") | getline' 如果您希望您的程序可移植到所有 awk 实现。

在这种情况下,管道之前的表达式是单个字符串,因此没有歧义。我将括号移到了更复杂的表达式所需要的位置。

此外,最好在while 循环退出后在命令上调用close()。如果有另一行匹配test,awk 将假定应该进一步阅读现有的子命令,除非它是close()d。由于命令匹配是通过字符串进行的,因此最好将其存储在变量中并将该变量用作close 的参数,而不是用括号括起来管道到getline 的左侧。例如:

awk '/^test / {
    cmd = sprintf("ping -c %d %s", $2, $3)
    while (cmd | getline > 0) print
    close(cmd)
}'

(一种没有分号的变体,有些人不喜欢:-)),当喂食时:

test 1 google.com

产生:

PING google.com (74.125.225.161): 56 data bytes
64 bytes from 74.125.225.161: icmp_seq=0 ttl=56 time=22.898 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 22.898/22.898/22.898/0.000 ms

附录(在网上四处寻找,我发现这没有我想象的那么明显):注意这种“裸”getline,因为它替换了“当前行”,导致任何剩余脚本中的模式和动作规则在 new 行内容上触发。例如,在上述之后,$0round-trip min/av 开头,因此 /^round/ 形式的后续规则将匹配,即使触发“ping”的输入行是 test 1 google.com。如果这不是最后一条规则,则向其添加 next 指令可能是合适的。 (在一个复杂的脚本中,我会将它放在每个 getline-ing 操作中,即使是最后一个,以防最后一条规则被移动或添加更多规则。)

【讨论】:

  • 太棒了!谢谢!!我曾尝试过 print 但不是 printf。有趣的是,只执行 printf($2) 会打印 HTML 输入文件的第二个字段,但 printf("",$2) 会打印 getline 的整行。我会继续调查,但现在我至少知道从哪里开始了!
  • 好的,我发现了我的主要错误:我将 getline 的输出重定向到一个变量中。这导致临时字段引用无效,因此所有 $2 表达式都引用了我的 HTML 输入文件。现在它可以正常工作了。再次感谢!!!
  • 另外,我对 printf("",$2) 的看法是错误的——它没有打印任何东西;我在脚本后面看到的是一个被遗忘的打印命令。
  • 这是调用 getline 的错误语法(缺少括号), printf 是内置函数而不是函数,并且 printf 行末尾的分号什么也不做。如果您正在考虑使用 getline,请确保您完全理解 awk.info/?tip/getline 的所有警告。使用 getline 通常是错误的方法,因此请确保您已经考虑清楚。到目前为止,我什至不明白问题是什么!
  • @torek 不,它们都需要括号,它们使 awk 在雨天场景中正常运行,而不是陷入无限循环。
【解决方案2】:

由于我最终的 awk 脚本的相关部分太大而无法作为评论,因此我将发布作为答案。 stripInputRecord、getIpNumber 和 getPortNumber 函数只是从 HTML 代码中挑选出有用的部分。

/address/ {
    ip = stripInputRecord( $0 );
    ip = getIpNumber( ip );
    port[na] = stripInputRecord( $0 );
    port[na] = getPortNumber( port[na] );
    if (!(ip~"N/A")) {
            if (ip~/loopback/) {
                    ip="127.0.0.1";
                    port[na]=stdp;
            }
            cmd="nmap -PN -p "stdp" "ip
            cmd2="nmap -PN -p " port[na] " " ip
            while ((cmd | getline)==1) {
                    if ($0~stdp) {
                            stdportstatus[na] = $2
                    }
            }
            while ((cmd2 | getline)==1) {
                    if ($0~port[na]) {
                            otherportstatus[na] = $2
                    }
            }
    }
    close(cmd)
    close(cmd2)
    if ($0~/N\/A/) {
            stdportstatus[na] = "-";
            otherportstatus[na] = "-";
    }
    na++;

}

谢谢大家(尤其是 torek!)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    相关资源
    最近更新 更多