0

我在这里找到了一些关于从 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 命令?

4

2 回答 2

3

使用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 手册中提到的一个完全不同的问题:

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

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

此外,最好在循环退出close()后调用命令。while如果有另一行匹配test,awk 将假定应该进一步阅读现有的子命令,除非它已被close()d。由于命令匹配是通过字符串进行的,因此将其存储在一个变量中并将该变量用作close. 例如:

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,因为它取代了“当前行”,导致任何剩余的模式和动作规则在脚本中触发行内容。例如,在上述之后,$0以 开头round-trip min/av,因此表单的后续规则/^round/将匹配,即使触发“ping”的输入行是test 1 google.com。如果这不是最后一条规则,则向其添加next指令可能是合适的。(在一个复杂的脚本中,我会将它放在每个getline-ing 动作中,即使是最后一个,以防最后一条规则被移动,或者添加更多规则。)

于 2013-08-24T06:36:26.343 回答
0

由于我最终的 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++;

}

谢谢大家(尤其是托雷克!)

于 2013-08-25T14:37:57.977 回答