bash – Разделить значения «таблицы» в строки в массиве

EDIT: Извините, результат, который я утверждал, ошибочен. Есть больше пробелов, чем я думал раньше (что-то произошло, когда вывод был сохранен в 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. Он работает так, как я хочу, но мне хотелось бы получить некоторую ежедневную статистику на жестких дисках, поэтому я создал свой собственный скрипт, который собирает информацию с smartmontools и другие интересные вещи (например, temps, значения SMART и место на жестком диске). Возможно, это не лучший способ сделать что-то подобное, но мне нравится это делать, и я учусь, когда я ухожу.

Письмо, которое я отправляю, отформатировано как HTML для создания таблиц и добавления цветов шрифта для положительных / отрицательных результатов (зеленый / красный). Но когда я попытался сделать одну таблицу для показа самотестирования, у меня появились некоторые проблемы.

Команда, которую я использую: sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE (в цикле, где $ 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' sudo smartctl --info $HDD | grep -e 'Serial Number' обычно выводит

Серийный номер: WD-RESTOFS / N123

Но, чтобы поместить его в таблицу, я разделил строку, используя символ «:», и получим массив следующим образом:

Серийный номер, WD-RESTOFS / N123

Но с выходом я получаю для sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE sudo smartctl -l selftest $HDD | grep '#' >> $SMARTFILE , нет (для меня) очевидного способа их разделить, и способ, которым я это делал раньше, не будет работать, поскольку строки, которые я хочу, имеют пробелы в них и поэтому не могут быть разделены с помощью пробела голец.

TL; DR, у меня есть следующая команда sudo smartctl -l selftest /dev/sda | grep '#' >> $SMARTFILE sudo smartctl -l selftest /dev/sda | grep '#' >> $SMARTFILE который имеет такой вывод:

# 1 Короткая в автономном режиме Завершено без ошибок 00% 7264 –

# 2 Короткое время автономной работы Завершено без ошибок 00% 7240 –

Я хочу сделать массив (или аналогичный) для их индивидуального хранения следующим образом:

# 1, Короткий оффлайн, Завершен без ошибок, 00%, 7264, –

Так что я могу легко поместить его в таблицу HTML. Это можно сделать? Если ошибка возникает, она может выглядеть примерно так:

# 1 Короткая в автономном режиме Завершено: сбой чтения 20% 717 555027747

Пожалуйста, дайте мне знать, если что-то неясно или есть какая-либо другая информация.

Из вашего (небольшого) образца сообщений 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 его переменной smartctl_output (строка из ваших образцов), и я присвоил результат переменной csv .

[обновление # 2]

Теперь кажется, что поля разделены двумя или более пробелами. Это еще проще, чем раньше. Команда sed становится:

 sed 's/ \+/,/g' 

Это означает: заменить все серии из двух или более пробелов двоеточием.

Я не могу придумать способ сделать это изначально в оболочке, но, например, в perl вы можете определить регулярное выражение для разбиения поля и использовать его для вставки единственного разделителя по вашему выбору, который затем можно было бы читать просто с помощью IFS=, или что угодно.

На основе вашего образца поля могут быть разделены на пробел, за которым следуют:

  1. символ верхнего регистра или дефис; или
  2. последовательность по меньшей мере двух цифр

Итак, прокладывайте свою команду что-то вроде

 . . . | perl -F'[[:space:]](?=[[:upper:]-]|[[:digit:]]{2,})' -anle 'print join ",", @F'