Fed*_*eri 4 bash awk text-processing
我有这个 bash 脚本:
gunzip -c /var/log/cisco/cisco.log-$(date +%Y%m%d).gz | awk '/ath_bstuck_tasklet/ { print $4 }' | sort | uniq -c > /tmp/netgear_beacon.txt
echo "There are `wc -l /tmp/netgear_beacon.txt | awk '{print $1}'` Stuck beacon; resetting" >> /tmp/netgear_beacon.txt
gunzip -c /var/log/cisco/cisco.log-`date +%Y%m%d`.gz | awk '/Virtual device ath0 asks to queue packet/ { print $4 }' | sort | uniq -c > /tmp/netgear_buffer_queue.txt
echo "There are `wc -l /tmp/netgear_buffer_queue.txt | awk '{print $1}'` routers with 'Virtual device ath0 asks to queue packet' errors" >> /tmp/netgear_buffer_queue.txt
gunzip -c /var/log/cisco/cisco.log-`date +%Y%m%d`.gz | awk '/CMS_MSG_DNSPROXY_RELOAD/ { print $4 }' | sort | uniq -c > /tmp/netgear_dns.txt
echo "There are `wc -l /tmp/netgear_dns.txt | awk '{print $1}'` routers with 'DNS Proxy Issue' errors" >> /tmp/netgear_dns.txt
gunzip -c /var/log/cisco/cisco.log-$(date +%Y%m%d).gz | awk '/beacon/ { print $4 }' | sort | uniq -c > /tmp/netgear_beacon_frame.txt
echo "There are `wc -l /tmp/netgear_beacon_frame.txt | awk '{print $1}'` routers with beacon frame errors" >> /tmp/netgear_beacon_frame.txt
gunzip -c /var/log/cisco/cisco.log-$(date +%Y%m%d).gz | awk '/ACK/ { print $4 }' | sort | uniq -c | awk -v x=50 '$1 >= x' > /tmp/netgear_ACK.txt
echo "There are `wc -l /tmp/netgear_ACK.txt | awk '{print $1}'` routers with more than 50 ACK" >> /tmp/netgear_ACK.txt
Run Code Online (Sandbox Code Playgroud)
我会尽量不要gunzip
每次都重复这个命令。我只会运行它一次并在所有步骤中使用它。我在想一个变量,但这是最佳实践吗?
PSk*_*cik 10
没有“最佳实践”。只有有意义的事情,让事情变得更容易。
提取公共部分并参数化其余部分就是这样一件事:
lines="`gunzip -c /var/log/cisco/cisco.log-$(date +%Y%m%d).gz`"
#gunzip would always output the same thing on the same day, so
#just run it once and store the results in a variable
grepAndLog(){
local regex="$1" file="$2" msg="$3" filter="${4:-cat}"
#^names for positional parameters
printf "%s\n" "$lines" | grep "$regex" | cut -d' ' -f4 | sort | uniq -c | eval "$filter" > "/tmp/$file"
local count=`wc -l < "/tmp/$file"`
echo "There are $count "" $msg" >> "/tmp/$file"
}
grepAndLog ath_bstuck_tasklet netgear_bacon.txt \
'Stuck beacon; resetting'
grepAndLog netgear_buffer_queue netgear_buffer_queue.txt \
"routers with 'Virtual device ath0 asks to queue packet' errors"
grepAndLog CMS_MSG_DNSPROXY_RELOAD netgear_dns.txt \
" routers with 'DNS Proxy Issue' errors"
grepAndLog ath_bstuck_tasklet netgear_bacon.txt \
" routers with beacon frame errors"
grepAndLog ACK netgear_ACK.txt \
" routers with more than 50 ACK" 'awk -v x=50 "\$1 >= x"'
Run Code Online (Sandbox Code Playgroud)
它仍然是一个主要的外壳解决方案。但 IMO 更具可读性,而且缩短了 40% 以上。
我正在使用grep "$regex" | cut -d' ' -f4
而不是 awk 表达式。除此之外,该grepAndLog
函数是您在脚本的每一行中所做操作的概括:您有一些输入(gunzip 的输出),您将其 grep 用于表达式($regex
参数),并输出结果行,排序和前缀与计数成一个$file
。然后,您将行数(我这样做wc -l < "$file"
而不是wc -l "$file" | awk ...
)附加在一条消息中,该消息的开头是恒定的,结尾是变化的 ( $msg
)。
在最后一行中,您不是简单地使用 grep,而是在此基础上使用另一个过滤器。在不存在第四个参数的正常情况下,我没有if
为其创建分支,而是简单地将其cat
用作隐式默认附加过滤器(local filter="${4:-cat}"
意味着创建一个函数局部变量过滤器,其内容是提供给函数的第四个参数,或者cat
如果没有提供第四个参数)。cat
如果给了第四个参数,则会被覆盖grepAndLog
。
在这里做的最好的事情是在单个awk
. 类似的东西:
gunzip -c /var/log/cisco/cisco.log-$(date +%Y%m%d).gz | awk '
/ath_bstuck_tasklet/ { netgear_beakon[$4] = 1 }
/Virtual device ath0 asks to queue packet/ { netgear_buffer_queue[$4] = 1 }
...
/ACK/ { netgear_ACK[$4] ++ }
END {
n=0; for(k in netgear_beakon) n++; print n,"Stuck beacon; resetting";
n=0; for(k in netgear_buffer_queue) n++; print n,"routers with Virtual device ath0 asks to queue packet";
...
n=0; for(k in netgear_ACK) n+=(netgear_ACK[k]>=50); print n,"routers with more than 50 ACK"
}'
Run Code Online (Sandbox Code Playgroud)
除了消除多次读取文件之外,这还消除了执行sort
和uniq
多次的需要。这将存储(或计算)数组中的每个唯一项,然后通过迭代每个数组的键来计算项数。