我有一个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