Обработка текста: sed, cut, tr, sort, uniq и paste
Автор: Казачкин Даниил Михайлович · Обновлено
Конвейер обработки текста полезен, когда нужно привести выгрузку к одному виду, выбрать поле и посчитать повторения. Каждая стадия должна иметь понятный вход и выход: иначе исправление одного значения незаметно меняет остальные. Здесь работаем с маленьким списком заявок, результат которого можно проверить глазами.
Подготовить данные и выделить столбец
Выполните блок в Bash. Временный каталог не пересекается с файлами проекта, разделитель в примере — один символ двоеточия.
text_lab=$(mktemp -d)
printf 'alice:open:12\nbob:closed:7\nalice:open:3\n' >"$text_lab/requests.txt"
cut -d ':' -f1 "$text_lab/requests.txt"
cut -d ':' -f2,3 "$text_lab/requests.txt"Первый результат — строки alice, bob, alice; второй сохраняет статус и число. В записи cut -d: -f1 опция -d задаёт разделитель, а -f1 выбирает первое поле, нумерация начинается с единицы. Без -d ожидается табуляция. Строка без разделителя по умолчанию проходит целиком; -s исключает такие строки. Для столбцов, разделённых переменным числом пробелов, обычно удобнее awk: два пробела у cut образуют пустое поле.
Изменить строки через sed
sed применяет команды к потоку и обычно пишет результат в stdout. Флаг g у подстановки меняет все совпадения внутри строки; без него заменяется только первое. Адрес перед командой ограничивает строки, к которым она применяется.
sed 's/:open:/:waiting:/g' "$text_lab/requests.txt"
sed -n '2p' "$text_lab/requests.txt"
sed '/^bob:/d' "$text_lab/requests.txt"
sed -i.bak 's/^bob:closed:/bob:review:/' "$text_lab/requests.txt"
diff -u "$text_lab/requests.txt.bak" "$text_lab/requests.txt" || true-n отключает обычную печать, поэтому 2p выводит только вторую строку. Последняя подстановка записывает результат и оставляет .bak; сравнение показывает замену одной строки. Чтобы заменить строку полностью, используйте, например, sed '2c\bob:review:7' файл. Точка и звёздочка в шаблоне — операторы регулярного выражения, а & в правой части означает найденное совпадение. Для буквальной замены пользовательских значений потребуется экранирование; произвольный ввод нельзя просто вставлять в программу sed.
Порядок и количество повторений
cut -d: -f1 "$text_lab/requests.txt" | LC_ALL=C sort | uniq -c
sort -t: -k3,3n "$text_lab/requests.txt"Первый конвейер выдаёт два появления alice и одно bob. uniq объединяет только соседние одинаковые строки, поэтому без сортировки первое и третье появления останутся раздельными. sort -u удобно для одного списка уникальных значений, но не даёт частоты. Числовой ключ -k3,3n сортирует третье поле как 3, 7, 12; обычная строковая сортировка могла бы поставить 12 перед 3. Локаль влияет на порядок букв и числовые форматы; LC_ALL=C делает техническую сортировку воспроизводимой.
Символы и склейка строк
printf 'a,b,c\n' | tr ',' '\n'
printf 'OK\r\n' | tr -d '\r'
printf 'alice\nbob\n' >"$text_lab/names"
printf '12\n7\n' >"$text_lab/counts"
paste -d: "$text_lab/names" "$text_lab/counts"tr заменяет или удаляет символы, а не слова: им нельзя корректно заменить слово cat на dog в предложении. Удаление \r применяйте к тексту с известными окончаниями строк, не к бинарному файлу. paste склеивает соответствующие строки и выдаёт alice:12, bob:7; при разной длине файлов появляются пустые поля. Это соединение по номеру строки, а не по ключу пользователя.
Ошибка и диагностика
Команда sed ... file >file обнулит вход ещё до чтения: перенаправление выполняет shell. Используйте отдельный результат и сравнение либо осознанное -i.bak. Если количество записей неожиданно изменилось, выполните стадии отдельно и сравните wc -l. Для CSV с кавычками и запятыми внутри значения нужны средства, понимающие CSV, а не механическое разделение по запятой.
Практика
Добавьте заявку bob:open:21. Получите число заявок каждого пользователя, затем строки в порядке возрастания третьего поля. Переименуйте только статус open, сохранив исходник. Проверьте, что число строк равно четырём, сумма чисел не изменилась, а .bak содержит старые статусы. Объясните, почему перестановка sort и uniq -c меняет ответ. Удалите учебный каталог после сравнения.
Что важно запомнить
Разделитель, регулярное выражение и порядок записей — часть формата данных. Проверяйте их до изменения файла; небольшая контрольная выборка помогает обнаружить потерю строк раньше, чем конвейер попадёт в автоматизацию.
Частые вопросы
Почему sed заменил больше текста, чем ожидалось?
Шаблон трактуется как регулярное выражение. Ограничьте начало или конец строки, проверьте метасимволы и сначала просмотрите stdout без записи исходника.
Можно ли считать уникальные IP через uniq?
Можно после извлечения IP и сортировки. Если нужен порядок первого появления, сортировка его уничтожает: используйте таблицу уже встреченных значений в awk.
Почему cut не подходит для произвольного CSV?
Разделитель внутри кавычек является частью поля. cut не хранит состояние кавычек и разделит такую запись неверно, даже если несколько простых примеров выглядят правильно.