![Почему разделитель полей awk работает некорректно?](https://rvso.com/image/154467/%D0%9F%D0%BE%D1%87%D0%B5%D0%BC%D1%83%20%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%20%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9%20awk%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82%20%D0%BD%D0%B5%D0%BA%D0%BE%D1%80%D1%80%D0%B5%D0%BA%D1%82%D0%BD%D0%BE%3F.png)
Я пытаюсь использовать awk с выводом ss, чтобы напечатать четвертый столбец. Иногда это работает, но иногда неправильно объединяет или разделяет столбцы. Я пробовал несколько разных вариантов для FS, здесь это два или более пробелов, потому что заголовки полей содержат один пробел.
Это дает мне пятый столбец и пустой заголовок:
$ ss -tn
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 36 172.31.19.34:22 172.115.128.85:64478
ESTAB 0 0 [::ffff:172.31.19.34]:80 [::ffff:172.115.128.85]:65446
$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'
172.115.128.86:64478
[::ffff:172.115.128.86]:65446
Та же команда здесь возвращает мне четвертый столбец, это то, что мне нужно.
$ ss -tn
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 36 172.31.19.34:22 172.115.128.85:64478
$ ss -tn | awk -F '[[:space:]][[:space:]]+' '{print $4}'
Local Address:Port
172.31.19.34:22
Я знаю, cut
что это может быть проще, но я использую его, awk
потому что хочу провести более подробную обработку.
Чтобы добавить подробности: я не уверен, почему ss показывает этот адрес в стиле IPv6. Это соединение моего ноутбука с сервером Apache, но у моего ноутбука нет адреса IPv6.
решение1
Какмурунамекнул вкомментарий, awk
вероятно, работает стабильно. Что может меняться, так это интервал в выводе ss
.
Оказывается, ss -nt
1 выводит семь столбцов, заголовки которых: State
, Recv-Q
, Send-Q
, Local Address
, Port
, Peer Address
, Port
. Четвертый и пятый столбцы разделены двоеточием ( :
); то же самое для шестого и седьмого. Все остальные разделены пробелом.
Все столбцы дополнены пробелами там, где это необходимо для выравнивания. Четвертый и шестой дополнены слева, все остальные — справа.
Дальнейшее наполнение может произойти:
Если вывод
ss -nt
направлен на терминал:если минимальная длина ее строк, вычисленная как сумма самого длинного содержимого для каждого поля плюс минимальный интервал (шесть символов), меньше ширины терминала, каждая строка расширяется до ширины терминала путем равномерного заполнения всех столбцов пробелами;
в противном случае строки разрываются, а поля выравниваются по строкам (с дополнением, как указано выше, до ширины терминала).
Если вывод
ss -nt
не направляется на терминал (например, он передается по конвейеру или перенаправляется в обычный файл), фактическая длина строк определяется как минимальное число, кратное 80, которое больше минимальной длины, определенной выше. Все столбцы равномерно заполняются пробелами для достижения общей длины строки, которая, следовательно, составит 80, 160, 240, ... символов 2 .
Таким образом, нет гарантии, что два столбца будут разделены двумя или более пробелами, что делает такую последовательность ненадежной для разделения.
Тем не менее, вы можете обрабатывать вывод ss -tn
достаточно безопасным способом, учитывая, что заголовки столбцов известны и фиксированы, и что, за исключением заголовков, ни один из столбцов не должен содержать пробелов 3 :
ss -nt | sed '
1 s/[ ]Address:/_Address|/g # Remove the known spaces from column
# headers; also, change ":" into "|"
s/:\([^:|]*[ ]\)/|\1/g # Change the colons used as separators
# into vertical bars "|", to avoid
s/:\([^:|]*\)$/|\1/g # confusion with those in IPv6s
' | awk -v FS='\\||[ ]+' -v OFS=":" ' # Split on sequences of one or more
{ print $4,$5 } # spaces OR on any vertical bar
'
Это напечатает только четвертый и пятый столбцы (локальный адрес и порт), разделенные двоеточием. Обратите внимание, что использование разделителя полей, который не является одинарным пробелом по умолчанию, awk
идентифицирует восемь столбцов вместо семи, и если вы используете { $1=$1; print; }
, он напечатает OFS
в конце любой строки, для которой последний столбец дополнен справа хотя бы одним пробелом.
1 Другие параметры (например -i
, , -e
, -m
) кардинально меняют вывод ss
. Для краткости и ясности мы сосредоточимся только на этой конкретной команде.
2 Приблизительно и, возможно, неточно. Но это не имеет отношения к сути этого вопроса/ответа.
3 По-видимому, это не гарантируется, мы намеренно не будем пытаться охватить все менее распространенные случаи.
решение2
Почему разделитель полей awk работает некорректно?
Ненадежным является количество пробелов в выходных данных ss
.
четвертая колонка, это то, что мне нужно.
Затем просто удалите заголовок ( -H
) и выберите четвертый столбец:
$ ss -taH | awk '{print $4}'
172.31.19.34:22
[::ffff:172.31.19.34]:80
Поскольку заголовок исправлен, просто добавьте его обратно (при необходимости):
$ echo "Local Address:Port"
Local Address:Port
Полная команда:
$ echo "Local Address:Port"; ss -tnH | awk '{print $4}'
Local Address:Port
172.31.19.34:22
[::ffff:172.31.19.34]:80
Да, ваш компьютер всегда имеет IPv6-адреса (один или несколько). Если вы не хотите их, запросите только IPv4-адреса:
$ ss -tnH4 | awk '{print $4}'
172.31.19.34:22