Почему вывод ss приходится фильтровать руками
Команда ss -tan на рабочей машине легко выдаёт десятки и сотни строк: браузер держит соединения с разными серверами, мессенджер и синхронизация файлов добавляют свои. Искать глазами нужный порт или адрес в такой простыне неудобно, и именно здесь выручает grep. Он читает поток построчно и пропускает только те строки, где нашлось совпадение с шаблоном.
У самой ss есть встроенные фильтры, например ss -tan state established, и они хороши, когда нужно одно состояние. Но стоит захотеть «установленные соединения с портом 443, кроме локальных», как проще собрать цепочку из нескольких простых команд, чем вспоминать синтаксис выражений ss. Конвейер читается слева направо, и каждый шаг можно проверить отдельно, убрав всё после него.
Дальше примеры на ss, потому что эта утилита есть в большинстве дистрибутивов Linux. В macOS её обычно нет, там те же приёмы работают с выводом netstat -an: меняется только написание состояний.
Как состояния называются в выводе
Первая ловушка: шаблон для grep зависит от того, какая утилита породила текст. В выводе ss состояния пишутся сокращённо, а в netstat полностью и с подчёркиванием. Если искать ESTABLISHED в выводе ss, не найдётся ни одной строки, и можно решить, что соединений нет.
| Состояние | В выводе ss | В выводе netstat |
|---|---|---|
| Соединение установлено | ESTAB | ESTABLISHED |
| Порт слушает | LISTEN | LISTEN |
| Ожидание после закрытия | TIME-WAIT | TIME_WAIT |
| Ждёт закрытия приложением | CLOSE-WAIT | CLOSE_WAIT |
| Отправлен SYN, ответа нет | SYN-SENT | SYN_SENT |
Чтобы не гадать, один раз выведите полный список и посмотрите на первый столбец. Если сомневаетесь в написании, шаблон можно сделать мягче: grep -i estab найдёт и ESTAB, и ESTABLISHED, независимо от регистра.
Ключи grep, которых хватает в большинстве случаев
Для работы с выводом сетевых команд обычно достаточно нескольких ключей. Их полезно знать наизусть, потому что дальше они комбинируются.
- -i игнорирует регистр букв в шаблоне.
- -v инвертирует выбор: остаются строки, где совпадения нет.
- -c печатает только число подходящих строк вместо самих строк.
- -n добавляет номер строки перед каждым совпадением.
- -w ищет шаблон как целое слово, а не как кусок слова.
- -E включает расширенные регулярные выражения: скобки, вертикальную черту, плюс.
- -F читает шаблон как обычный текст без спецсимволов, что удобно для адресов с точками.
- -o печатает только совпавшую часть строки, а не всю строку.
Ключи можно склеивать: grep -ci estab считает строки без учёта регистра. Для остальных ключей, включая вывод строк контекста до и после совпадения, смотрите справку grep или man grep: набор опций немного различается между GNU grep и версией, которая идёт с macOS.
Готовые примеры на выводе ss -tan
Ниже рабочие цепочки. Адреса из блоков документации 192.0.2.0/24 и 198.51.100.0/24 здесь взяты как условные.
- ss -tan | grep ESTAB — только установленные соединения.
- ss -tan | grep -c ESTAB — сколько их, одним числом.
- ss -tan | grep LISTEN — какие порты принимают входящие подключения.
- ss -tan | grep ESTAB | grep -E ':443( |$)' — установленные соединения с портом 443: после номера должен идти пробел или конец строки.
- ss -tan | grep -E ':(80|443)( |$)' — порты 80 и 443 одним шаблоном.
- ss -tan | grep ESTAB | grep -v '127.0.0.1' — без локальных соединений самого компьютера по адресу 127.0.0.1 (IPv6-адрес ::1 так не отсеять).
- ss -tan | grep -F '198.51.100.7' — все строки, где встречается один адрес.
Пример строки условный, у вас формат может отличаться. Строка вывода выглядит примерно так: ESTAB 0 0 192.0.2.10:52144 198.51.100.7:443. Слева локальный адрес с портом, справа удалённый. Если нужен только порт или только адрес, а не вся строка, grep уже неудобен: для вырезания столбцов лучше подойдёт awk, об этом отдельная страница.
Где grep вводит в заблуждение
Распространённая причина странных результатов — подстрока. Шаблон :443 совпадёт и с портом 443, и с портом 4430, и с хвостом IPv6-адреса. Поэтому перед номером порта ставят двоеточие, которое отделяет его от адреса, а после номера требуют пробел или конец строки: ':443( |$)'. Просто пробел в конце шаблона не подходит, потому что удалённый порт в ss стоит последним в строке, и после него пробела нет. С IPv6 строки получаются длиннее и с большим числом двоеточий, так что проверяйте результат глазами хотя бы на одном примере.
Вторая особенность: заголовок таблицы тоже строка. Если вы считаете строки без grep, например через wc -l, к итогу добавится одна лишняя строка с названиями столбцов. Шаблон ESTAB заголовок не захватит, поэтому счёт через grep -c получается точнее.
Третья: код возврата. Когда ни одна строка не подошла, grep завершается с кодом 1, и в скриптах с проверкой ошибок это выглядит как сбой, хотя команда отработала штатно. Наконец, сам grep иногда попадает в вывод ps, если вы ищете процесс, но к ss это не относится.
Если результат выглядит неправдоподобно, уберите последнее звено цепочки и посмотрите на вывод до него: так проще понять, на каком шаге строки пропали.
Как собрать фильтр под свою задачу
Удобный порядок такой: сначала посмотреть сырой вывод и найти в нём строку, которая нужна, потом добавлять по одному звену. Каждое звено сужает выбор, и по числу оставшихся строк видно, что именно отсеялось.
Например, нужно понять, сколько соединений держит один адрес. Сначала выполните ss -tan | grep -F '198.51.100.7' и убедитесь, что адрес вообще встречается. Затем допишите | grep -c ESTAB и сравните число с количеством строк без второго фильтра. Разница покажет соединения в других состояниях, например TIME-WAIT: они уже закрыты и просто доживают положенное время.
Кавычки вокруг шаблона нужны, когда в нём есть пробел, точка с запятой, скобки или вертикальная черта: оболочка иначе попытается истолковать их по-своему. Одинарные кавычки защищают шаблон надёжнее двойных, потому что внутри них оболочка ничего не подставляет.
Если нужно повторять проверку каждую секунду, цепочку целиком можно отдать команде watch, а для долгого наблюдения за файлом журнала подходит tail -f. Обе команды разобраны на отдельных страницах этого раздела.