#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