如何编写 bash 脚本以填充文本表的空字段



我有一个bash脚本,它生成一个表作为输出,如下所示:

Name                 IP Address        IPv6 Address                     Address Source   Connection Type
Inferno              192.168.0.12      N/A                              DHCP             Ethernet 
VirgilioMarone       192.168.0.10      N/A                              DHCP             Ethernet 
RE305                192.168.0.2       N/A                              DHCP             Wi-Fi 
Google-Nest-Hub      192.168.0.100     N/A                              DHCP             Wi-Fi 
iPaddiG              192.168.0.216     N/A                              DHCP             Wi-Fi 
*                                      N/A                              DHCP             Wi-Fi 
Alighieri            192.168.0.13      N/A                              DHCP             Wi-FI

我需要确定一种算法,该算法接收上表并将"-"放在字段为空的地方,如下所示(我不复制另一行只是为了简化文本):

Name              IP Address        IPv6 Address                     Address Source   Connection Type
*              -                 N/A                              DHCP             Wi-Fi 5GHz

附言:文本之间没有制表符;只有空格。

我会使用第一行来获取每列的预期大小。

head -1 /path/to/file |
grep -oE '[^ ]([^ ]| [^ ])* *  ' |
awk '{print length($0)}'
  • head -1仅保留第一行
  • grep -oE '[^ ]([^ ]| [^ ])* * '捕获所有以非空格字符开头的字符串,[^ ]后跟任何没有两个连续空格的字符串([^ ]| [^ ])*并以至少有两个空格*的空格序列结尾;当心正则表达式中的确切字符,否则它根本不起作用
  • awk '{print length($0)}'只是显示每个序列的长度

请注意,最后一列丢失,因为它不以 2+ 空格结尾。这是一件好事,因为我认为最后一列不应限制在固定宽度。

对于您的示例,它输出:

21
18
33
17

获得此列表后,您可以将其存储到数组中,例如:

mapfile -t widths < <(head -1 /path/to/file |
grep -oE '[^ ]([^ ]| [^ ])* *  ' |
awk '{print length($0)}')

然后,您可以读取每一行并使用每列的固定宽度将其拆分:

while IFS= read -r line || [[ -n "$line" ]]; do
t=0;
for x in "${widths[@]}"; do
s=${line:$t:$x};
if [[ "$s" =~ ^ *$ ]]; then
printf %s "${s/ /-}";
else
printf -- %s "$s";
fi;
((t+=x));
done;
s=${line:$t};
if [ -z "$s" ]; then
printf -- -\n;
else
if [[ "$s" =~ ^ *$ ]]; then
printf %s\n "${s/ /-}";
else
printf -- %s\n "$s";
fi;
fi;
done < /path/to/file
  • while IFS= read -r line || [[ -n "$line" ]]; do ... done < /path/to/file读取$line变量中的每一行;使用-r选项读取非常重要,这样就不会解释转义序列,并设置IFS=以便不修剪前导和尾随空格;|| [[ -n "$line" ]]奖励是一个技巧,它修复了read的限制,如果文件的最后一行不以换行符结尾,则返回 false
  • 到目前为止,该行中读取的字符总数t
  • for x in "${widths[@]}"; do ... done遍历之前计算的widths数组
  • s=${line:$t:$x}从索引t获取字符到索引t+x,换句话说,它从行中获取一列保留尾随的空格字符,这就是魔术发生的地方,因为一旦你拥有它你就赢了
  • if [[ "$s" =~ ^ *$ ]]; then printf %s "${s/ /-}"; else printf -- %s "$s"; fi;检查列是否完全为空,在这种情况下,它会调用printf %s "${s/ /-}",输出第一个字符替换为连字符的字符串,同时保持字段宽度不变(您可以对printf -- "%-${x}s" -执行相同的操作),否则它会按原样打印字符串printf -- %s "$s",确保使用--否则如果字符串"$s"以连字符开头,您将收到错误
  • ((t+=x))将变量t前进,以便下一列从正确的索引开始
  • 然后s=${line:$t}获取从索引t开始到行尾的最后一列,即没有固定宽度
  • 最后一列的打印与其他列几乎
  • 相同,除了我们处理空列的情况(这在前面的列上不会发生),并且我们以\n结束输出以输出换行符

如果一切顺利,它应该显示:

Name                 IP Address        IPv6 Address                     Address Source   Connection Type
Inferno              192.168.0.12      N/A                              DHCP             Ethernet
VirgilioMarone       192.168.0.10      N/A                              DHCP             Ethernet
RE305                192.168.0.2       N/A                              DHCP             Wi-Fi
Google-Nest-Hub      192.168.0.100     N/A                              DHCP             Wi-Fi
iPaddiG              192.168.0.216     N/A                              DHCP             Wi-Fi
*                    -                 N/A                              DHCP             Wi-Fi
Alighieri            192.168.0.13      N/A                              DHCP             Wi-FI

相关内容

  • 没有找到相关文章

最新更新