为什么 awk 字段分隔符不能一致工作?

为什么 awk 字段分隔符不能一致工作?

我正在尝试使用 awk 和 ss 的输出来打印第四列。有时它会起作用,但有时它会错误地合并或拆分列。我尝试了几种不同的 FS 选项,这里是两个或多个空格,因为字段标题包含一个空格。

这给了我第五列和一个空白标题:

$ ss -tn
State   Recv-Q    Send-Q                Local Address:Port                   Peer Address:Port     
ESTAB   0         36                     172.31.19.34:22                   172.115.128.85:64478    
ESTAB   0         0             [::ffff:172.31.19.34]:80          [::ffff:172.115.128.85]:65446    


$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'

172.115.128.86:64478 
[::ffff:172.115.128.86]:65446 

这里相同的命令给了我第四列,这就是我想要的。

$ ss -tn
State     Recv-Q      Send-Q              Local Address:Port               Peer Address:Port       
ESTAB     0           36                   172.31.19.34:22               172.115.128.85:64478   

$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'
Local Address:Port
172.31.19.34:22

我知道cut可能更容易,但我正在使用,awk因为我想做进一步的处理。

添加细节:我不确定为什么 ss 显示此 IPv6 样式地址。这是从我的笔记本电脑到 apache 服务器的连接,但我的笔记本电脑没有 IPv6 地址。

答案1

作为穆鲁暗示在一个评论awk可能会持续工作。可以变化的是 的输出中的间距ss

结果ss -nt1输出了七列,标题分别是:State, Recv-Q, Send-Q, Local Address, Port, Peer Address, Port。第四列和第五列之间用冒号 ( :) 分隔;第六和第七也是同样的情况。所有其他内容均由空格字符分隔。
所有列都在需要对齐的地方填充了空格。第四个和第六个填充在其左侧,所有其他填充在右侧。

可能会发生进一步的填充:

  1. 如果 的输出ss -nt定向到终端:

    1. 如果其行的最小长度(计算为每个字段的最长内容加上最小间距(六个字符)之和)小于终端的宽度,则每行通过均匀填充扩展到终端的宽度所有带有空格的列;

    2. 否则,线会被打断,并且字段会跨线对齐(如上填充,直到终端的宽度)。

  2. 如果 的输出ss -nt不定向到终端(例如,它通过管道传输或重定向到常规文件),则行的实际长度定义为 80 的最小倍数,该倍数高于上面定义的最小长度。所有列均均匀地填充空格,以达到总行长度,即 80、160、240、...字符2

因此,不能保证两列将被两个或多个空格分隔,从而使得该序列对于分割来说不可靠。

尽管如此,您仍然可以以相当安全的方式处理 的输出ss -tn,注意列标题​​是已知且固定的,并且除了标题之外,其任何列都不应包含空格3

ss -nt | sed '
  1 s/[ ]Address:/_Address|/g           # Remove the known spaces from column
                                        # headers; also, change ":" into "|"
  s/:\([^:|]*[ ]\)/|\1/g                # Change the colons used as separators
                                        # into vertical bars "|", to avoid
  s/:\([^:|]*\)$/|\1/g                  # confusion with those in IPv6s
' | awk -v FS='\\||[ ]+' -v OFS=":" '   # Split on sequences of one or more
  { print $4,$5 }                       # spaces OR on any vertical bar
'

这将仅打印第四和第五列(本地地址和端口),以冒号分隔。请注意,使用不是默认的单个空格的字段分隔符awk将识别八列而不是七列,并且如果您执行 a { $1=$1; print; },它将OFS在最后一列右侧填充有 at 的任何行的末尾打印一个至少有一个空格。


1其他选项(例如-i, -e, -m)会极大地改变 的输出ss。为了简洁和清楚起见,我们将只关注这个确切的命令。
2近似且可能不精确。但这与这个问题/答案的要点无关。
3显然这并不能得到保证,我们故意不尝试涵盖所有不常见的情况。

答案2

为什么 awk 字段分隔符不能一致工作?

是的,不可靠的是 的输出中的空格数量ss

第四栏,这就是我想要的。

然后只需删除标题(-H)并选择第四列:

$ ss -taH | awk '{print $4}'
172.31.19.34:22
[::ffff:172.31.19.34]:80

由于标头是固定的,只需将其添加回来(如果需要):

$  echo "Local Address:Port"
Local Address:Port

完整命令:

$ echo "Local Address:Port"; ss -tnH | awk '{print $4}'
Local Address:Port
172.31.19.34:22
[::ffff:172.31.19.34]:80

是的,您的计算机始终具有 IPv6(一个或多个)地址。如果您不需要它们,只需询问 IPv4 地址:

$ ss -tnH4 | awk '{print $4}'
172.31.19.34:22

相关内容