编辑:对不起,我声称的输出是错误的。空格比我之前想象的要多(当输出保存到html文件以删除这些空格时发生了一些事情)真正的输出如下:
user@Debian:~$ sudo smartctl -l selftest /dev/sda | grep -e "#"
# 1 Short offline Completed without error 00% 7264 -
# 2 Short offline Completed without error 00% 7240 -
# 3 Short offline Completed without error 00% 7219 -
# 4 Short offline Completed without error 00% 7192 -
# 5 Short offline Completed without error 00% 7168 -
# 6 Short offline Completed without error 00% 7144 -
# 7 Extended offline Completed without error 00% 7125 -
# 8 Short offline Completed without error 00% 7096 -
# 9 Short offline Completed without error 00% 7072 -
#10 Short offline Completed without error 00% 7049 -
#11 Short offline Completed without error 00% 7004 -
我不确定我是否使用了正确的术语,因为我对 Linux/bash 相当陌生。
不管怎样,我正在使用 Smartmontools 来检测并通知我是否有任何 SMART 错误。它按照我想要的方式工作,但我想获得有关 HDD 的一些日常统计信息,因此我制作了自己的脚本,从 smartmontools 和其他有趣的东西(如临时、SMART 值和使用的 HDD 空间)收集信息。可能不是做这样的事情的最好方法,但我喜欢这样做,并且我正在边做边学。
我发送的电子邮件采用 HTML 格式来制作表格并添加正面/负面结果的字体颜色(绿色/红色)。但当我尝试制作一张用于显示自测试的表格时,我遇到了一些问题。
我使用的命令是:(sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE
在一个循环中,$HDD 是我系统中的所有 HDD,$SMARTFILE 是我将其保存到的 html 文件。
该命令的输出如下所示:
# 1 短暂离线 已完成且没有错误 00% 7264 -
# 2 短暂离线 已完成且无错误 00% 7240 -
等等。我使用以下代码来获取驱动器的序列号:
HDDinfo="$(sudo smartctl --info $HDD | grep -e 'Serial Number')"
IFS=':' read -r -a array <<< "$HDDinfo"
由于sudo smartctl --info $HDD | grep -e 'Serial Number'
通常输出
序列号:WD-RESTOFS/N123
但为了将其放入表中,我使用“:”字符分隔字符串并获得如下数组:
序列号,WD-RESTOFS/N123
但是对于我得到的输出sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE
,(对我来说)没有明显的方法来分隔它们,而且我之前所做的方式不起作用,因为我想要的字符串中有空格,因此不能使用空格字符分隔。
TL;DR,我有以下命令sudo smartctl -l selftest /dev/sda | grep '#' >> $SMARTFILE
,其输出如下:
# 1 短暂离线 已完成且没有错误 00% 7264 -
# 2 短暂离线 已完成且无错误 00% 7240 -
我想创建一个数组(或类似的数组)来单独存储它们,如下所示:
# 1,短暂离线,完成无错误,00%,7264,-
这样我就可以轻松地将其放入 HTML 表格中。这可以做到吗?如果确实发生错误,它可能看起来像这样:
#1 短离线完成:读取失败20% 717 555027747
如果有不清楚的地方或需要任何其他信息,请告诉我。
答案1
从上面的(小)smartctl
消息样本来看,它们的各个部分似乎基本上是由“<空格><除小写之外的任何内容>”分隔的(除了行开头的“# nnn”字段)。
sed
可以帮助分离零件:
$ smartctl_output="\
# 1 Short offline Completed without error 00% 7264 -
# 2 Short offline Completed without error 00% 7240 -
# 1 Short offline Completed: read failure 20% 717 555027747"
$ csv="$( sed 's/ //; s/ \([^[:lower:]]\)/,\1/g' <<< "$smartctl_output" )"
$ echo "$csv"
#1,Short offline,Completed without error,00%,7264,-
#2,Short offline,Completed without error,00%,7240,-
#1,Short offline,Completed: read failure,20%,717,555027747
如果这是您想要的,您现在可以像使用 HDDinfo 一样填充阵列。
[更新]
sed
这是对进行拆分的部分的解释:该sed
程序由我放在一行上的两部分组成。这是扩展版本:
sed '
s/ //
s/ \([^[:lower:]]\)/,\1/g
'
程序sed
对每一行输入进行操作:它读取一行,应用一组转换,然后打印该行。然后它从下一行开始,直到没有更多的行可供读取。
这里第一个sed
命令s/ //
删除第一个空格以将“#”和后面的数字放在一起。
然后,第二个sed
命令s/ \([^[:lower:]]\)/,\1/g
搜索每个字段的开头(由“<空格><小写字母以外的任何内容>”定义)并用冒号替换空格。指\1
的是括号“ ”之间的正则表达式\([^[:lower:]]\)
,它代表下一个字段的第一个字符。
剩下的部分是一个测试:sed
我没有输入文件的内容或命令的输出,而是输入了变量smartctl_output
(由样本组成的字符串),并将结果分配给了该csv
变量。
[更新#2]
现在看来,这些字段是由两个或多个空格分隔的。它甚至比以前更容易。命令sed
变为:
sed 's/ \+/,/g'
这意味着:用冒号替换所有两个或多个空格的系列。
答案2
我想不出一种方法可以在 shell 中本地执行此操作,但是perl
例如您可以定义一个正则表达式用于字段分割,并使用它插入您选择的单个分隔符,然后可以简单地使用IFS=,
或其他方式读取该分隔符。
根据您的示例,字段可能会按空格分割,后跟以下任一内容:
- 大写字符或连字符;或者
- 至少两个数字的序列
所以通过管道传递你的命令
. . . |
perl -F'[[:space:]](?=[[:upper:]-]|[[:digit:]]{2,})' -anle 'print join ",", @F'