使用sed/awk解析ethtool输出的Shell脚本优化问询
问题描述
我正在编写Shell脚本,需要从ethtool命令输出中解析网卡的支持链路模式(Supported link modes)与通告链路模式(Advertised link modes),并与预期值匹配——要求预期值需同时存在于两种模式中才满足条件。
现有ethtool输出示例:
# ethtool eth5 Settings for eth1: Supported ports: [ Backplane ] Supported link modes: 1000baseKX/Full 10000baseKR/Full 15000baseKR/Full Supported pause frame use: Symmetric Supports auto-negotiation: Yes Supported FEC modes: None BaseR RS Advertised link modes: 1000baseKX/Full 10000baseKR/Full 15000baseKR/Full
我当前使用的脚本代码如下:
link_mode_expected=15000baseKR # get the mode using the shell command and parse the value needed mode_supported=`ethtool ${eth_device} | sed -ne '/Supported link modes:/,/:/p' |\ sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep "$link_mode_expected"` mode_advertised=`ethtool ${eth_device} | sed -ne '/Advertised link modes:/,/:/p' |\ sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep "$link_mode_expected"` if [ "$mode_supported" != "$link_mode_expected" ] ||\ ( [ "$mode_supported" != "$mode_advertised" ] ) ; then #some action fi
当前代码可正常获取结果(两种模式均返回15000baseKR/Full),但有以下优化需求:
- 如何避免重复执行两次
ethtool命令,能否将输出存入缓存后再进行匹配? - 实现相同功能的awk等价写法是什么?awk的执行效率是否更高?
- 是否存在比当前方案更优的实现思路?
解决方案
1. 缓存ethtool输出,避免重复执行
把ethtool的输出一次性存入变量,之后从变量里提取内容即可,无需重复调用命令:
link_mode_expected=15000baseKR eth_device=eth5 # 一次性获取ethtool输出并缓存 ethtool_output=$(ethtool "$eth_device") # 从缓存中提取支持的模式 mode_supported=$(echo "$ethtool_output" | sed -ne '/Supported link modes:/,/:/p' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep "$link_mode_expected") # 从缓存中提取通告的模式 mode_advertised=$(echo "$ethtool_output" | sed -ne '/Advertised link modes:/,/:/p' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | grep "$link_mode_expected") if [ -z "$mode_supported" ] || [ -z "$mode_advertised" ]; then # 预期模式不同时存在于两种模式中,执行动作 echo "Expected link mode $link_mode_expected not found in both supported and advertised modes" # some action fi
这里还优化了判断逻辑:直接检查两个变量是否为空,只要有一个为空,就说明预期模式不存在,符合需求。
2. awk等价写法及效率对比
用awk可以一次性完成解析和判断,无需多次管道调用,代码更简洁,执行效率也更高(减少了进程创建的开销):
link_mode_expected=15000baseKR eth_device=eth5 ethtool "$eth_device" | awk -v expected="$link_mode_expected" ' /Supported link modes:/ { in_supported=1; next } in_supported && /:/ { in_supported=0 } in_supported { if ($0 ~ expected) supported=1 } /Advertised link modes:/ { in_advertised=1; next } in_advertised && /:/ { in_advertised=0 } in_advertised { if ($0 ~ expected) advertised=1 } END { if (!(supported && advertised)) { print "Expected link mode " expected " not found in both modes" exit 1 # 返回非0状态码,方便后续脚本判断 } } ' # 根据awk的返回值执行后续动作 if [ $? -ne 0 ]; then # some action fi
效率对比:
awk是单进程处理,而原脚本(包括缓存后的版本)会创建多个sed、grep进程,所以awk的执行效率明显更高,尤其是在需要批量处理多个网卡时,差异会更明显。
3. 更优的实现思路
可以进一步简化逻辑,直接在awk中完成所有判断,甚至不需要额外变量存储输出,同时可以更精确地匹配模式(避免部分匹配的问题,比如预期1000baseKX时不会匹配到10000baseKR):
link_mode_expected=15000baseKR eth_device=eth5 # 精确匹配完整模式行(包含/Full后缀) ethtool "$eth_device" | awk -v expected="$link_mode_expected/Full" ' BEGIN { supported=0; advertised=0 } /Supported link modes:/ { section="supported"; next } /Advertised link modes:/ { section="advertised"; next } /:/ { section="" } # 离开模式区域 section != "" { gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0) # 去除前后空格 if ($0 == expected) { if (section == "supported") supported=1 else if (section == "advertised") advertised=1 } } END { exit !(supported && advertised) } ' if [ $? -ne 0 ]; then echo "Expected link mode $link_mode_expected is not fully supported and advertised" # some action fi
另外,也可以用grep一次性匹配两个模式区域,但可读性不如awk,且需要预估模式行数量(-A5表示取匹配行后5行),如果模式数量超过这个值会漏判:
link_mode_expected=15000baseKR eth_device=eth5 ethtool_output=$(ethtool "$eth_device") # 检查是否同时存在于两个区域 if ! grep -q -A5 "Supported link modes:" <<< "$ethtool_output" | grep -q "$link_mode_expected" || \ ! grep -q -A5 "Advertised link modes:" <<< "$ethtool_output" | grep -q "$link_mode_expected"; then # some action fi
内容的提问来源于stack exchange,提问作者pradiptart
相关产品推荐
相关产品推荐

