Как закодировать скрипт bash для заполнения пустых полей текстовой таблицы

#bash #shell #replace

#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
 

Мне нужно определить алгоритм, который получает приведенную выше таблицу и помещает «-«, где поле имеет значение null, как это (я не копирую другую строку только для упрощения текста):

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

PS между текстом нет табуляции; только пробелы.

Комментарии:

1. Знаете ли вы количество символов в каждом поле?

2. Пожалуйста, опубликуйте скрипт bash, вероятно, это можно было бы сделать там.

3. Да, было бы лучше исправить это в исходном скрипте, а не пытаться исправить вывод.

4. Если первое поле всегда равно 7 столбцам, вы можете использовать sed с регулярным выражением, которое соответствует любым 7 символам в начале строки, за которыми следует пробел, и заменяет его этими 7 символами, за которыми следует - . Используйте группу захвата, чтобы получить содержимое первого поля и скопировать его на замену.

5. — Нет, количество символов в каждом поле меняется динамично. — Сценарий bash является результатом запроса команды маршрутизатора, я не могу изменить прошивку … или, лучше, я не хочу тратить на это энергию 🙂

Ответ №1:

Я бы использовал первую строку для получения ожидаемого размера каждого столбца.

 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 опцией, чтобы не интерпретировались escape-последовательности, и устанавливать 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