让awk对系统命令的结果采取行动

时间:2013-08-24 06:16:48

标签: awk

我是Stack Overflow处女(好吧,直到现在)所以请和我一起露出......

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

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

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

提前谢谢!

此致 安德斯

2 个答案:

答案 0 :(得分: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 manuals中提到的一个完全不同的问题:

  

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

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

此外,在close()循环退出后,在命令上调用while是个好主意。如果有另一行匹配test,awk将假定应该进一步读取现有的子命令,除非它已经close() d。由于命令匹配是通过字符串进行的,所以更好,而不是将管道到getline的左侧括起来,将其存储在变量中,并将该变量用作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,因为它取代了“当前行”,导致脚本中任何剩余的模式和操作规则,用于触发行内容。例如,在上述情况之后,$0round-trip min/av开头,因此/^round/形式的后续规则将匹配,即使触发“ping”的输入行为{{1} }。如果这不是最后一条规则,则可能需要向其添加test 1 google.com指令。 (在一个复杂的脚本中,我将它放在每个next动作中,即使是最后一个动作,以防移动最后一条规则,或者添加更多规则。)

答案 1 :(得分: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++;

}

谢谢大家(特别是torek!)