Как найти слово в файле на linux

Иногда может понадобится найти файл, в котором содержится определённая строка или найти строку в файле, где есть нужное слово. В Linux для этого существует несколько утилит, одна из самых используемых это grep. С её помощью можно искать не только строки в файлах, но и фильтровать вывод команд, и много чего ещё.

В этой инструкции мы рассмотрим что такое команда grep Linux, подробно разберём синтаксис и возможные опции grep, а также приведём несколько примеров работы с этой утилитой.

Что такое grep?

Название команды grep расшифровывается как «search globally for lines matching the regular expression, and print them». Это одна из самых востребованных команд в терминале Linux, которая входит в состав проекта GNU. До того как появился проект GNU, существовала утилита предшественник grep, тем же названием, которая была разработана в 1973 году Кеном Томпсоном для поиска файлов по содержимому в Unix. А потом уже была разработана свободная утилита с той же функциональностью в рамках GNU.

Grep дает очень много возможностей для фильтрации текста. Вы можете выбирать нужные строки из текстовых файлов, отфильтровать вывод команд, и даже искать файлы в файловой системе, которые содержат определённые строки. Утилита очень популярна, потому что она уже предустановлена прочти во всех дистрибутивах.

Синтаксис grep

Синтаксис команды выглядит следующим образом:

$ grep [опции] шаблон [/путь/к/файлу/или/папке…]

Или:

$ команда | grep [опции] шаблон

Здесь:

  • Опции — это дополнительные параметры, с помощью которых указываются различные настройки поиска и вывода, например количество строк или режим инверсии.
  • Шаблон — это любая строка или регулярное выражение, по которому будет выполняться поиск.
  • Имя файла или папки — это то место, где будет выполняться поиск. Как вы увидите дальше, grep позволяет искать в нескольких файлах и даже в каталоге, используя рекурсивный режим.

Возможность фильтровать стандартный вывод пригодится, например, когда нужно выбрать только ошибки из логов или отфильтровать только необходимую информацию из вывода какой-либо другой утилиты.

Опции

Давайте рассмотрим самые основные опции утилиты, которые помогут более эффективно выполнять поиск текста в файлах grep:

  • -E, —extended-regexp — включить расширенный режим регулярных выражений (ERE);
  • -F, —fixed-strings — рассматривать шаблон поиска как обычную строку, а не регулярное выражение;
  • -G, —basic-regexp — интерпретировать шаблон поиска как базовое регулярное выражение (BRE);
  • -P, —perl-regexp — рассматривать шаблон поиска как регулярное выражение Perl;
  • -e, —regexp — альтернативный способ указать шаблон поиска, опцию можно использовать несколько раз, что позволяет указать несколько шаблонов для поиска файлов, содержащих один из них;
  • -f, —file — читать шаблон поиска из файла;
  • -i, —ignore-case — не учитывать регистр символов;
  • -v, —invert-match — вывести только те строки, в которых шаблон поиска не найден;
  • -w, —word-regexp — искать шаблон как слово, отделенное пробелами или другими знаками препинания;
  • -x, —line-regexp — искать шаблон как целую строку, от начала и до символа перевода строки;
  • -c — вывести количество найденных строк;
  • —color — включить цветной режим, доступные значения: never, always и auto;
  • -L, —files-without-match — выводить только имена файлов, будут выведены все файлы в которых выполняется поиск;
  • -l, —files-with-match — аналогично предыдущему, но будут выведены только файлы, в которых есть хотя бы одно вхождение;
  • -m, —max-count — остановить поиск после того как будет найдено указанное количество строк;
  • -o, —only-matching — отображать только совпавшую часть, вместо отображения всей строки;
  • -h, —no-filename — не выводить имя файла;
  • -q, —quiet — не выводить ничего;
  • -s, —no-messages — не выводить ошибки чтения файлов;
  • -A, —after-content — показать вхождение и n строк после него;
  • -B, —before-content — показать вхождение и n строк после него;
  • -C — показать n строк до и после вхождения;
  • -a, —text — обрабатывать двоичные файлы как текст;
  • —exclude — пропустить файлы имена которых соответствуют регулярному выражению;
  • —exclude-dir — пропустить все файлы в указанной директории;
  • -I — пропускать двоичные файлы;
  • —include — искать только в файлах, имена которых соответствуют регулярному выражению;
  • -r — рекурсивный поиск по всем подпапкам;
  • -R — рекурсивный поиск включая ссылки;

Все самые основные опции рассмотрели, теперь давайте перейдём к примерам работы команды grep Linux.

Примеры использования grep

Давайте перейдём к практике. Сначала рассмотрим несколько основных примеров поиска внутри файлов Linux с помощью grep.

1. Поиск текста в файле

В первом примере мы будем искать информацию о пользователе root в файле со списком пользователей Linux /etc/passwd. Для этого выполните следующую команду:

grep root /etc/passwd

В результате вы получите что-то вроде этого:

С помощью опции -i можно указать, что регистр символов учитывать не нужно. Например, давайте найдём все строки содержащие вхождение слова time в том же файле:

grep -i "time" /etc/passwd

В этом случае Time, time, TIME и другие вариации слова будут считаться эквивалентными. Ещё, вы можете указать несколько условий для поиска, используя опцию -e. Например:

grep -e "root" -e "daemon" /etc/passwd

C помощью опции -n можно выводить номер строки, в которой найдено вхождение, например:

grep -n 'root' /etc/passwd

Это всё хорошо работает пока ваш поисковый запрос не содержит специальных символов. Например, если вы попытаетесь найти все строки, которые содержат символ «[» в файле /etc/grub/00_header, то получите ошибку, что это регулярное выражение не верно. Для того чтобы этого избежать, нужно явно указать, что вы хотите искать строку с помощью опции -F:

grep -F "[" /etc/grub.d/00_header

Теперь вы знаете как выполняется поиск текста файлах grep.

2. Фильтрация вывода команды

Для того чтобы отфильтровать вывод другой команды с помощью grep достаточно перенаправить его используя оператор |. А файл для самого grep указывать не надо. Например, для того чтобы найти все процессы gnome можно использовать такую команду:

ps aux | grep "gnome"

В остальном всё работает аналогично.

3. Базовые регулярные выражения

Утилита grep поддерживает несколько видов регулярных выражений. Это базовые регулярные выражения (BRE), которые используются по умолчанию и расширенные (ERE). Базовые регулярные выражение поддерживает набор символов, позволяющих описать каждый определённый символ в строке. Это: ., *, [], [^], ^ и $. Например, вы можете найти строки, которые начитаются на букву r:

grep "^r" /etc/passwd

Или же строки, которые содержат большие буквы:

grep "[A-Z]" /etc/passwd

А так можно найти все строки, которые заканчиваются на ready в файле /var/log/dmesg:

grep "ready$" /var/log/dmesg

Но используя базовый синтаксис вы не можете указать точное количество этих символов.

4. Расширенные регулярные выражения

В дополнение ко всем символам из базового синтаксиса, в расширенном синтаксисе поддерживаются также такие символы:

  • + — одно или больше повторений предыдущего символа;
  • ? — ноль или одно повторение предыдущего символа;
  • {n,m} — повторение предыдущего символа от n до m раз;
  • | — позволяет объединять несколько паттернов.

Для активации расширенного синтаксиса нужно использовать опцию -E. Например, вместо использования опции -e, можно объединить несколько слов для поиска вот так:

grep -E "root|daemon" /etc/passwd

Вообще, регулярные выражения grep — это очень обширная тема, в этой статье я лишь показал несколько примеров. Как вы увидели, поиск текста в файлах grep становиться ещё эффективнее. Но на полное объяснение этой темы нужна целая статья, поэтому пока пропустим её и пойдем дальше.

5. Вывод контекста

Иногда бывает очень полезно вывести не только саму строку со вхождением, но и строки до и после неё. Например, мы хотим выбрать все ошибки из лог-файла, но знаем, что в следующей строчке после ошибки может содержаться полезная информация, тогда с помощью grep отобразим несколько строк. Ошибки будем искать в /var/log/dmesg по шаблону «Error»:

grep -A4 "Error" /var/log/dmesg

Выведет строку с вхождением и 4 строчки после неё:

grep -B4 "Error" /var/log/dmesg

Эта команда выведет строку со вхождением и 4 строчки до неё. А следующая выведет по две строки с верху и снизу от вхождения.

grep -C2 "Error" /var/log/dmesg

6. Рекурсивный поиск в grep

До этого мы рассматривали поиск в определённом файле или выводе команд. Но grep также может выполнить поиск текста в нескольких файлах, размещённых в одном каталоге или подкаталогах. Для этого нужно использовать опцию -r. Например, давайте найдём все файлы, которые содержат строку Kernel в папке /var/log:

grep -r "Kernel" /var/log

Папка с вашими файлами может содержать двоичные файлы, в которых поиск выполнять обычно не надо. Для того чтобы их пропускать используйте опцию -I:

grep -rI "Kernel" /var/log

Некоторые файлы доступны только суперпользователю и для того чтобы выполнять по ним поиск вам нужно запускать grep с помощью sudo. Или же вы можете просто скрыть сообщения об ошибках чтения и пропускать такие файлы с помощью опции -s:

grep -rIs "Kernel" /var/log

7. Выбор файлов для поиска

С помощью опций —include и —exclude вы можете фильтровать файлы, которые будут принимать участие в поиске. Например, для того чтобы выполнить поиск только по файлам с расширением .log в папке /var/log используйте такую команду:

grep -r --include="*.log" "Kernel" /var/log

А для того чтобы исключить все файлы с расширением .journal надо использовать опцию —exclude:

grep -r --exclude="*.journal" "Kernel" /var/log

8. Поиск слов в grep

Когда вы ищете строку abc, grep будет выводить также kbabc, abc123, aafrabc32 и тому подобные комбинации. Вы можете заставить утилиту искать по содержимому файлов в Linux строки, которые включают только искомые слова полностью с помощью опции -w. Например:

grep -w "root" /etc/passwd

9. Количество строк

Утилита grep может сообщить, сколько строк с определенным текстом было найдено файле. Для этого используется опция -c (счетчик). Например:

grep -c 'Kernel' /var/log/dmesg

10. Инвертированный поиск

Команда grep Linux может быть использована для поиска строк, которые не содержат указанное слово. Например, так можно вывести только те строки, которые не содержат слово nologin:

grep -v nologin /etc/passwd

11. Вывод имен файлов

Вы можете указать grep выводить только имена файлов, в которых было хотя бы одно вхождение с помощью опции -l. Например, следующая команда выведет все имена файлов из каталога /var/log, при поиске по содержимому которых было обнаружено вхождение Kernel:

grep -lr 'Kernel' /var/log/

12. Цветной вывод

По умолчанию grep не будет подсвечивать совпадения цветом. Но в большинстве дистрибутивов прописан алиас для grep, который это включает. Однако, когда вы используйте команду c sudo это работать не будет. Для включения подсветки вручную используйте опцию —color со значением always:

sudo grep --color=always root /etc/passwd

Получится:

Выводы

Вот и всё. Теперь вы знаете что представляет из себя команда grep Linux, а также как ею пользоваться для поиска файлов и фильтрации вывода команд. При правильном применении эта утилита станет мощным инструментом в ваших руках. Если у вас остались вопросы, пишите в комментариях!

Обнаружили ошибку в тексте? Сообщите мне об этом. Выделите текст с ошибкой и нажмите Ctrl+Enter.

Creative Commons License

Статья распространяется под лицензией Creative Commons ShareAlike 4.0 при копировании материала ссылка на источник обязательна .

Команда grep означает «печать глобального регулярного выражения», и это одна из самых мощных и часто используемых команд в Linux.

grep ищет в одном или нескольких входных файлах строки, соответствующие заданному шаблону, и записывает каждую соответствующую строку в стандартный вывод. Если файлы не указаны, grep читает из стандартного ввода, который обычно является выводом другой команды.

В этой статье мы покажем вам, как использовать команду grep на практических примерах и подробных объяснениях наиболее распространенных опций GNU grep .

Командный синтаксис grep

Синтаксис команды grep следующий:

grep [OPTIONS] PATTERN [FILE...]

Пункты в квадратных скобках необязательны.

  • OPTIONS — Ноль или более вариантов. Grep включает ряд опций , управляющих его поведением.
  • PATTERN — Шаблон поиска.
  • FILE — Ноль или более имен входных файлов.

Чтобы иметь возможность искать файл, пользователь, выполняющий команду, должен иметь доступ для чтения к файлу.

Искать строку в файлах

Наиболее простое использование команды grep — поиск строки (текста) в файле.

Например, чтобы отобразить все строки, содержащие строку bash из файла /etc/passwd , вы должны выполнить следующую команду:

grep bash /etc/passwd

Результат должен выглядеть примерно так:

root:x:0:0:root:/root:/bin/bash
linuxize:x:1000:1000:linuxize:/home/linuxize:/bin/bash

Если в строке есть пробелы, вам нужно заключить ее в одинарные или двойные кавычки:

grep "Gnome Display Manager" /etc/passwd

Инвертировать соответствие (исключить)

Чтобы отобразить строки, не соответствующие шаблону, используйте параметр -v (или --invert-match ).

Например, чтобы распечатать строки, не содержащие строковый nologin вы должны использовать:

grep -v nologin /etc/passwd
root:x:0:0:root:/root:/bin/bash
colord:x:124:124::/var/lib/colord:/bin/false
git:x:994:994:git daemon user:/:/usr/bin/git-shell
linuxize:x:1000:1000:linuxize:/home/linuxize:/bin/bash

Использование Grep для фильтрации вывода команды

Вывод команды может быть отфильтрован с помощью grep через конвейер, и на терминал будут напечатаны только строки, соответствующие заданному шаблону.

Например, чтобы узнать, какие процессы выполняются в вашей системе как пользовательские www-data вы можете использовать следующую команду ps :

ps -ef | grep www-data
www-data 18247 12675  4 16:00 ?        00:00:00 php-fpm: pool www
root     18272 17714  0 16:00 pts/0    00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn www-data
www-data 31147 12770  0 Oct22 ?        00:05:51 nginx: worker process
www-data 31148 12770  0 Oct22 ?        00:00:00 nginx: cache manager process

Вы также можете объединить несколько каналов по команде. Как вы можете видеть в выходных данных выше, также есть строка, содержащая процесс grep . Если вы не хотите, чтобы эта строка отображалась, передайте результат другому экземпляру grep как показано ниже.

ps -ef | grep www-data | grep -v grep
www-data 18247 12675  4 16:00 ?        00:00:00 php-fpm: pool www
www-data 31147 12770  0 Oct22 ?        00:05:51 nginx: worker process
www-data 31148 12770  0 Oct22 ?        00:00:00 nginx: cache manager process

Рекурсивный поиск

Для рекурсивного поиска шаблона вызовите grep с параметром -r (или --recursive ). Когда используется этот параметр, grep будет искать все файлы в указанном каталоге, пропуская символические ссылки, которые встречаются рекурсивно.

Чтобы следовать по всем символическим ссылкам , вместо -r используйте параметр -R (или --dereference-recursive ).

Вот пример, показывающий, как искать строку linuxize.com во всех файлах внутри каталога /etc :

grep -r linuxize.com /etc

Вывод будет включать совпадающие строки с префиксом полного пути к файлу:

/etc/hosts:127.0.0.1 node2.linuxize.com
/etc/nginx/sites-available/linuxize.com:    server_name linuxize.com   www.linuxize.com;

Если вы используете опцию -R , grep будет следовать по всем символическим ссылкам:

grep -R linuxize.com /etc

Обратите внимание на последнюю строку вывода ниже. Эта строка не печатается, когда grep вызывается с -r потому что файлы внутри каталога с sites-enabled Nginx являются символическими ссылками на файлы конфигурации внутри каталога с sites-available .

/etc/hosts:127.0.0.1 node2.linuxize.com
/etc/nginx/sites-available/linuxize.com:    server_name linuxize.com   www.linuxize.com;
/etc/nginx/sites-enabled/linuxize.com:    server_name linuxize.com   www.linuxize.com;

Показать только имя файла

Чтобы подавить вывод grep по умолчанию и вывести только имена файлов, содержащих совпадающий шаблон, используйте параметр -l (или --files-with-matches ).

Приведенная ниже команда выполняет поиск по всем файлам, заканчивающимся на .conf в текущем рабочем каталоге и выводит только имена файлов, содержащих строку linuxize.com :

grep -l linuxize.com *.conf

Результат будет выглядеть примерно так:

tmux.conf
haproxy.conf

Параметр -l обычно используется в сочетании с рекурсивным параметром -R :

grep -Rl linuxize.com /tmp

Поиск без учета регистра

По умолчанию grep чувствителен к регистру. Это означает, что символы верхнего и нижнего регистра рассматриваются как разные.

Чтобы игнорировать регистр при поиске, вызовите grep с параметром -i (или --ignore-case ).

Например, при поиске Zebra без какой-либо опции следующая команда не покажет никаких результатов, т.е. есть совпадающие строки:

grep Zebra /usr/share/words

Но если вы выполните поиск без учета регистра с использованием параметра -i , он будет соответствовать как заглавным, так и строчным буквам:

grep -i Zebra /usr/share/words

Указание «Зебра» будет соответствовать «зебре», «ZEbrA» или любой другой комбинации букв верхнего и нижнего регистра для этой строки.

zebra
zebra's
zebras

Искать полные слова

При поиске строки grep отобразит все строки, в которых строка встроена в строки большего размера.

Например, если вы ищете «gnu», все строки, в которых «gnu» встроено в слова большего размера, такие как «cygnus» или «magnum», будут найдены:

grep gnu /usr/share/words
cygnus
gnu
interregnum
lgnu9d
lignum
magnum
magnuson
sphagnum
wingnut

Чтобы вернуть только те строки, в которых указанная строка представляет собой целое слово (заключенное в символы, отличные от слов), используйте параметр -w (или --word-regexp ).

Символы слова включают буквенно-цифровые символы ( az , AZ и 0-9 ) и символы подчеркивания ( _ ). Все остальные символы считаются несловесными символами.

Если вы запустите ту же команду, что и выше, включая параметр -w , команда grep вернет только те строки, где gnu включен как отдельное слово.

grep -w gnu /usr/share/words
gnu

Показать номера строк

Параметр -n (или --line-number ) указывает grep показывать номер строки, содержащей строку, соответствующую шаблону. Когда используется эта опция, grep выводит совпадения на стандартный вывод с префиксом номера строки.

Например, чтобы отобразить строки из файла /etc/services содержащие строку bash префиксом совпадающего номера строки, вы можете использовать следующую команду:

grep -n 10000 /etc/services

Результат ниже показывает нам, что совпадения находятся в строках 10423 и 10424.

10423:ndmp            10000/tcp
10424:ndmp            10000/udp

Подсчет совпадений

Чтобы вывести количество совпадающих строк в стандартный вывод, используйте параметр -c (или --count ).

В приведенном ниже примере мы подсчитываем количество учетных записей, в которых в качестве оболочки используется /usr/bin/zsh .

regular expressiongrep -c '/usr/bin/zsh' /etc/passwd
4

Бесшумный режим

-q (или --quiet ) указывает grep работать в тихом режиме, чтобы ничего не отображать на стандартном выводе. Если совпадение найдено, команда завершает работу со статусом 0 . Это полезно при использовании grep в сценариях оболочки, где вы хотите проверить, содержит ли файл строку, и выполнить определенное действие в зависимости от результата.

Вот пример использования grep в тихом режиме в качестве тестовой команды в операторе if :

if grep -q PATTERN filename
then
    echo pattern found
else
    echo pattern not found
fi

Основное регулярное выражение

GNU Grep имеет три набора функций регулярных выражений : базовый, расширенный и Perl-совместимый.

По умолчанию grep интерпретирует шаблон как базовое регулярное выражение, где все символы, кроме метасимволов, на самом деле являются регулярными выражениями, которые соответствуют друг другу.

Ниже приведен список наиболее часто используемых метасимволов:

  • Используйте символ ^ (каретка) для сопоставления выражения в начале строки. В следующем примере строка kangaroo будет соответствовать только в том случае, если она встречается в самом начале строки.

     grep "^kangaroo" file.txt
  • Используйте символ $ (доллар), чтобы найти выражение в конце строки. В следующем примере строка kangaroo будет соответствовать только в том случае, если она встречается в самом конце строки.

     grep "kangaroo$" file.txt
  • Используйте расширение . (точка) символ, соответствующий любому одиночному символу. Например, чтобы сопоставить все, что начинается с kan затем имеет два символа и заканчивается строкой roo , вы можете использовать следующий шаблон:

     grep "kan..roo" file.txt
  • Используйте [ ] (скобки) для соответствия любому одиночному символу, заключенному в квадратные скобки. Например, найдите строки, содержащие accept или « accent , вы можете использовать следующий шаблон:

     grep "acce[np]t" file.txt
  • Используйте [^ ] для соответствия любому одиночному символу, не заключенному в квадратные скобки. Следующий шаблон будет соответствовать любой комбинации строк, содержащих co(any_letter_except_l)a , например coca , cobalt и т. Д., Но не будет соответствовать строкам, содержащим cola ,

     grep "co[^l]a" file.txt

Чтобы избежать специального значения следующего символа, используйте символ (обратная косая черта).

Расширенные регулярные выражения

Чтобы интерпретировать шаблон как расширенное регулярное выражение, используйте параметр -E (или --extended-regexp ). Расширенные регулярные выражения включают в себя все основные метасимволы, а также дополнительные метасимволы для создания более сложных и мощных шаблонов поиска. Вот несколько примеров:

  • Сопоставьте и извлеките все адреса электронной почты из данного файла:

     grep -E -o "b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,6}b" file.txt
  • Сопоставьте и извлеките все действительные IP-адреса из данного файла:

     grep -E -o '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' file.txt

Параметр -o используется для печати только соответствующей строки.

Поиск нескольких строк (шаблонов)

Два или более шаблонов поиска можно объединить с помощью оператора ИЛИ | .

По умолчанию grep интерпретирует шаблон как базовое регулярное выражение, в котором метасимволы, такие как | теряют свое особое значение, и необходимо использовать их версии с обратной косой чертой.

В приведенном ниже примере мы ищем все вхождения слов fatal , error и critical в файле ошибок журнала Nginx :

grep 'fatal|error|critical' /var/log/nginx/error.log

Если вы используете опцию расширенного регулярного выражения -E , то оператор | не следует экранировать, как показано ниже:

grep -E 'fatal|error|critical' /var/log/nginx/error.log

Строки печати перед матчем

Чтобы напечатать определенное количество строк перед совпадающими строками, используйте параметр -B (или --before-context ).

Например, чтобы отобразить пять строк ведущего контекста перед совпадающими строками, вы должны использовать следующую команду:

grep -B 5 root /etc/passwd

Печатать строки после матча

Чтобы напечатать определенное количество строк после совпадающих строк, используйте параметр -A (или --after-context ).

Например, чтобы отобразить пять строк конечного контекста после совпадающих строк, вы должны использовать следующую команду:

grep -A 5 root /etc/passwd

Выводы

Команда grep позволяет искать шаблон внутри файлов. Если совпадение найдено, grep печатает строки, содержащие указанный шаблон.

Подробнее о Grep можно узнать на странице руководства пользователя Grep .

Если у вас есть какие-либо вопросы или отзывы, не стесняйтесь оставлять комментарии.

Здесь представлен фрагмент будущей книги «Основные инструменты и практики для начинающего разработчика программного обеспечения» Бальтазара Рубероля и Этьена Броду. Книга должна помочь образованию подрастающего поколения разработчиков. Она охватит такие темы, как освоение консоли, настройка и эффективная работа в командной оболочке, управление версиями кода с помощью git, основы SQL, инструменты вроде Make, jq и регулярные выражения, основы сетевого взаимодействия, а также лучшие практики разработки программного обеспечения и совместной работы. В настоящее время авторы упорно работают над этим проектом и приглашают всех поучаствовать в списке рассылки.

Содержание

  • cat
  • head
  • tail
  • wc
  • grep
  • cut
  • paste
  • sort
  • uniq
  • awk
  • tr
  • fold
  • sed
  • Реальные примеры
  • Углубляемся: циклы for и xargs
  • Итоги
  • Что дальше

Обработка текста в командной оболочке

Одна из причин, которые делают командную оболочку бесценным инструментом, — это большое количество команд обработки текста и возможность легко объединять их в конвейер, создавая сложные шаблоны обработки. Эти команды делают тривиальными многие задачи по анализу текста и данных, преобразованию данных между разными форматами, по фильтрации строк и т. д.

При работе с текстовыми данными главный принцип заключается в том, чтобы разбить любую сложную проблему на множество более мелких — и решить каждую из них с помощью специализированного инструмента.

Заставьте каждую программу хорошо выполнять одну функцию — «Основы философии Unix»

Примеры из этой главы на первый взгляд могут показаться немного надуманными, но это сделано специально. Каждый из инструментов разработан для решения одной небольшой задачи. Однако в сочетании они становятся чрезвычайно мощными.

Мы рассмотрим некоторые из наиболее распространенных и полезных команд обработки текста в командной оболочке и продемонстрируем реальные рабочие процессы, соединяющие их вместе. Я предлагаю взглянуть на маны этих команд, чтобы увидеть всю широту возможностей в вашем распоряжении.

Файл CSV с примерами доступен в онлайне. Можете скачать его для проверки материала.

cat

Команда cat используется для составления списка из одного или нескольких файлов и отображения их содержимого на экране.

$ cat Documents/readme
Thanks again for reading this book!
I hope you're following so far!

$ cat Documents/computers
Computers are not intelligent
They're just fast at making dumb things.

$ cat Documents/readme Documents/computers
Thanks again for reading this book!
I hope you are following so far!

Computers are not intelligent
They're just fast at making dumb things.

head

head выводит первые n строк в файле. Это может быть очень полезно для того, чтобы заглянуть в файл неизвестной структуры и формата, не заваливая всю консоль кучей текста.

$ head -n 2 metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera cluster.,0,mysql,galera cluster size

Если -n не указано, head выводит первые десять строк указанного файла или входящего потока.

tail

tail — аналог head, только он выводит последние n строк в файле.

$ tail -n 1 metadata.csv
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

Если хотите вывести все строки, расположенном после n-й строки (включая её), можете использовать аргумент -n +n.

$ tail -n +42 metadata.csv
mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication master.,0,mysql,slaves connected
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

В нашем файле 43 строки, поэтому tail -n +42 выводит только 42-ю и 43-ю строки из него.

Если параметр -n не указан, tail выведет последние десять строк в указанном файле или входном потоке.

tail -f или tail --follow отображают последние строки в файле и каждую новую строку по мере записи в файл. Это очень полезно для просмотра активности в реальном времени, например, что записывается в логи веб-сервера и т. д.

wc

wc (word count) выводит количество символов (-c), слов (-w) или строк (-l) в указанном файле или потоке.

$ wc -l metadata.csv
43  metadata.csv
$ wc -w metadata.csv
405 metadata.csv
$ wc -c metadata.csv
5094 metadata.csv

По умолчанию отображается всё вышеперечисленное.

$ wc metadata.csv
43     405    5094 metadata.csv

Если текстовые данные передаются по конвейеру или перенаправлены в stdin, то отображается только счётчик.

$ cat metadata.csv | wc
43     405    5094
$ cat metadata.csv | wc -l
43
$ wc -w < metadata.csv
405

grep

grep — это швейцарский нож фильтрации строк по заданному шаблону.

Например, можем найти все вхождения слова mutex в файле.

$ grep mutex metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin rounds.,0,mysql,mutex spin rounds
mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin waits.,0,mysql,mutex spin waits

grep может обрабатывать либо файлы, указанные в качестве аргументов, либо поток текста, переданный на его stdin. Таким образом, мы можем сцеплять несколько команд grep для дальнейшей фильтрации текста. В следующем примере мы фильтруем строки в нашем файле metadata.csv, чтобы найти строки, содержащие и mutex, и OS.

$ grep mutex metadata.csv | grep OS
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits

Рассмотрим некоторые опции grep и их поведение.

grep -v выполняет инвертное сопоставление: фильтрует строки, которые не соответствуют шаблону аргументов.

$ grep -v gauge metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name

grep -i выполняет сопоставление без учёта регистра. В следующем примере grep -i os находит как OS, так и os.

$ grep -i os metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log file.,0,mysql,log fsyncs

grep -l выводит список файлов, содержащих совпадение.

$ grep -l mysql metadata.csv
metadata.csv

Команда grep -c подсчитывает, сколько раз найден образец.

$ grep -c select metadata.csv
3

grep -r рекурсивно ищет файлы в текущем рабочем каталоге и всех его подкаталогах.

$ grep -r are ~/Documents
/home/br/Documents/computers:Computers are not intelligent
/home/br/Documents/readme:I hope you are following so far!

grep -w показывает только совпадающие целиком слова.

$ grep follow ~/Documents/readme
I hope you are following so far!
$ grep -w follow ~/Documents/readme
$

cut

cut извлекает часть файла (или, как обычно, входного потока). Команда определяет разделитель полей (который разделяет столбцы) с помощью опции -d, а порядковые номера столбцов для извлечения с помощью опции -f.

Например, следующая команда извлекает первый столбец из последних пяти строк нашего CSV-файла.

$ tail -n 5 metadata.csv | cut -d , -f 1
mysql.performance.user_time
mysql.replication.seconds_behind_master
mysql.replication.slave_running
mysql.replication.slaves_connected
mysql.performance.queries

Поскольку мы имеем дело с CSV, то столбцы разделяются запятой, а за извлечение первого столбца отвечает опция -f 1.

Можно выбрать и первый, и второй столбцы, используя опцию -f 1,2.

$ tail -n 5 metadata.csv | cut -d , -f 1,2
mysql.performance.user_time,gauge
mysql.replication.seconds_behind_master,gauge
mysql.replication.slave_running,gauge
mysql.replication.slaves_connected,gauge
mysql.performance.queries,gauge

paste

paste объединяет вместе два разных файла в один многоколоночный файл.

$ cat ingredients
eggs
milk
butter
tomatoes
$ cat prices
1$
1.99$
1.50$
2$/kg
$ paste ingredients prices
eggs    1$
milk    1.99$
butter  1.50$
tomatoes    2$/kg

По умолчанию paste использует разделитель табуляции, но его можно изменить с помощью параметра -d.

$ paste ingredients prices -d:
eggs:1$
milk:1.99$
butter:1.50$
tomatoes:2$/kg

Ещё один распространённый способ использования paste — объединение всех строк в потоке или файле с помощью заданного разделителя, используя комбинацию аргументов -s и -d.

$ paste -s -d, ingredients
eggs,milk,butter,tomatoes

Если в качестве входного файла указан параметр -, то вместо него будет считываться stdin.

$ cat ingredients | paste -s -d, -
eggs,milk,butter,tomatoes

sort

Команда sort, собственно, сортирует данные (в указанном файле или входном потоке).

$ cat ingredients
eggs
milk
butter
tomatoes
salt
$ sort ingredients
butter
eggs
milk
salt
tomatoes

sort -r выполняет обратную сортировку.

$ sort -r ingredients
tomatoes
salt
milk
eggs
butter

sort -n сортирует поля по их арифметическому значению.

$ cat numbers
0
2
1
10
3
$ sort numbers
0
1
10
2
3
$ sort -n numbers
0
1
2
3
10

uniq

uniq обнаруживает и отфильтровывает соседние одинаковые строки в указанном файле или входном потоке.

$ cat duplicates
and one
and one
and two
and one
and two
and one, two, three
$ uniq duplicates
and one
and two
and one
and two
and one, two, three

Поскольку uniq отфильтровывает только соседние строки, в наших данных могут ещё остаться дубликаты. Чтобы отфильтровать все одинаковые строки из файла, нужно сначала отсортировать его содержимое.

$ sort duplicates | uniq
and one
and one, two, three
and two

uniq -c в начале каждой строки вставляет количество её вхождений.

$ sort duplicates | uniq -c
   3 and one
   1 and one, two, three
   2 and two

uniq -u отображает только уникальные строки.

$ sort duplicates | uniq -u
and one, two, three

Примечание. uniq особенно полезен в сочетании с сортировкой, поскольку конвейер | sort | uniq позволяет удалить все дублирующиеся строки в файле или потоке.

awk

awk — это чуть больше, чем просто инструмент обработки текста: на самом деле у него целый язык программирования. В чём awk действительно хорош — так это в разбиении файлов на столбцы, и делает это с особенным блеском, когда в файлах перемешаны пробелы и табы.

$ cat -t multi-columns
John Smith    Doctor^ITardis
Sarah-James Smith^I    Companion^ILondon
Rose Tyler   Companion^ILondon

Примечание. cat -t отображает табы как ^I.

Как видим, столбцы разделены либо пробелами, либо табуляциями, и не всегда одинаковым количеством пробелов. cut здесь бесполезен, потому что работает только с одним символом-разделителем. Но awk легко разберётся с таким файлом.

awk '{ print $n }' выводит n-й столбец в тексте.

$ cat multi-columns | awk '{ print $1 }'
John
Sarah-James
Rose
$ cat multi-columns | awk '{ print $3 }'
Doctor
Companion
Companion
$ cat multi-columns | awk '{ print $1,$2 }'
John Smith
Sarah-James Smith
Rose Tyler

Хотя awk способен на гораздо большее, выдача колонок составляет, наверное, 99% вариантов использования в моём личном случае.

Примечание. { print $NF } выводит последний столбец в строке.

tr

tr расшифровывается как translate. Эта команда заменяет одни символы на другие. Она работает либо с символами, либо с классами символов, такими как строчные, печатные, пробелы, буквенно-цифровые и т. д.

На стандартных входных данных tr <char1> <char2> заменяет все вхождения <char1> на <char2>.

$ echo "Computers are fast" | tr a A
computers Are fAst

tr может переводить классы символов с помощью нотации [:class:]. Полный список доступных классов описан на справочной странице tr, но некоторые продемонстрируем здесь.

[:space:] представляет все типы пробелов, от простого пробела до табуляции или символа новой строки.

$ echo "computers are fast" | tr '[:space:]' ','
computers,are,fast,%

Все символы, похожие на пробелы, переведены в запятую. Обратите внимание, что символ % в конце выдачи означает отсутствие завершающей новой строки. Действительно, этот символ тоже переведён в запятую.

[:lower:] представляет все строчные символы, а [:upper:] — все прописные. Таким образом, преобразование между ними становится тривиальным.

$ echo "computers are fast" | tr '[:lower:]' '[:upper:]'
COMPUTERS ARE FAST
$ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]'
computers are fast

tr -c SET1 SET2 преобразует любой символ, не входящий в набор SET1, в символы набора SET2. В следующем примере все символы, кроме указанных гласных, заменяются пробелами.

$ echo "computers are fast" | tr -c '[aeiouy]' ' '
 o  u e   a e  a

tr -d удаляет указанные символы, а не заменяет их. Это эквивалент tr <char> ''.

$ echo "Computers Are Fast" | tr -d '[:lower:]'
C A F

tr также может заменить диапазоны символов, например, все буквы между a и e или все числа между 1 и 8, используя нотацию s-e, где s — начальный символ, а e — конечный.

$ echo "computers are fast" | tr 'a-e' 'x'
xomputxrs xrx fxst
$ echo "5uch l337 5p34k" | tr '1-4' 'x'
5uch lxx7 5pxxk

Команда tr -s string1 сжимает все множественные вхождения символов в string1 в одно-единственное. Одним из наиболее полезных применений tr -s является замена нескольких последовательных пробелов одним.

$ echo "Computers         are       fast" | tr -s ' '
Computers are fast

fold

Команда fold сворачивает все входные строки до заданной ширины. Например, может быть полезно убедиться, что текст помещается на дисплеях небольшого размера. Так, fold -w n укладывает строки по ширине n символов.

$ cat ~/Documents/readme | fold -w 16
Thanks again for
 reading this bo
ok!
I hope you're fo
llowing so far!

Команда fold -s будет разбивать строки только на символах пробела. Её можно объединить с предыдущей, чтобы ограничить строким заданным количеством символом.

Thanks again
for reading
this book!
I hope you're
following so
far!

sed

sed — это неинтерактивный потоковый редактор, который используется для преобразования текста во входном потоке строка за строкой. В качестве входных данных используется или файл, или stdin, а на выходе тоже или файл, или stdout.

Команды редактора могут включать один или несколько адресов, функцию и параметры. Таким образом, команды выглядят следующим образом:

[address[,address]]function[arguments]

Хотя sed выполняет множество функций, мы рассмотрим только замену текста как один из самых распространённых вариантов использования.

Замена текста

Команда замены sed выглядит следующим образом:

s/PATTERN/REPLACEMENT/[options]

Пример: замена первого экземпляра слова в каждой строке в файле:

$ cat hello
hello hello
hello world!
hi
$ cat hello | sed 's/hello/Hey I just met you/'
Hey I just met you hello
Hey I just met you world
hi

Мы видим, что в первой строчке заменяется только первый экземпляр hello. Чтобы заменить все вхождения hello во всех строках, можно использовать опцию g (означает global).

$ cat hello | sed 's/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

sed позволяет использовать любые разделители, кроме /, что особенно улучшает читаемость, если в самих аргументах команды есть слэши.

$ cat hello | sed 's@hello@Hey I just met you@g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

Адрес говорит редактору, в какой строке или диапазоне строк выполнять подстановку.

$ cat hello | sed '1s/hello/Hey I just met you/g'
Hey I just met you hello
hello world
hi
$ cat hello | sed '2s/hello/Hey I just met you/g'
hello hello
Hey I just met you  world
hi

Адрес 1 указывает заменять hello на Hey I just met you в первой строке. Можем указать диапазон адресов в нотации <start>,<end>, где <end> может быть либо номером строки, либо $, то есть последней строкой в файле.

$ cat hello | sed '1,2s/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
$ cat hello | sed '2,3s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
$ cat hello | sed '2,$s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi

По умолчанию sed выдаёт результат в свой stdout, но может отредактировать и оригинальный файл с опцией -i.

$ sed -i '' 's/hello/Bonjour/' sed-data
$ cat sed-data
Bonjour hello
Bonjour world
hi

Примечание. В Linux достаточно только -i. Но в macOS поведение команды немного отличается, поэтому сразу после -i нужно добавить ''.

Реальные примеры

Фильтрация CSV с помощью grep и awk

$ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }'
mysql.performance.com_delete per second
mysql.performance.com_delete_multi per second
mysql.performance.com_insert per second
mysql.performance.com_insert_select per second
mysql.performance.com_replace_select per second
mysql.performance.com_select per second
mysql.performance.com_update per second
mysql.performance.com_update_multi per second
mysql.performance.questions per second
mysql.performance.slow_queries per second
mysql.performance.queries per second

В этом примере grep в файле metadata.csv сначала фильтрует строки, содержащие слово gauge, затем те, у которых query в четвёртой колонке, и выводит название метрики (1-я колонка) с соответствующим значением per_unit_name (5-я колонка).

Вывод адреса IPv4, связанного с сетевым интерфейсом

$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

Команда ifconfig <interface name> выводит сведения по указанному сетевому интерфейсу. Например:

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether 19:64:92:de:20:ba
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

Затем запускаем grep для inet, что выдаст две строки соответствия.

$ ifconfig en0 | grep inet
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Затем с помощью grep -v исключаем строку с ipv6.

$ ifconfig en0 | grep inet | grep -v inet6
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Наконец, с помощью awk запрашиваем второй столбец в этой строке: это IPv4-адрес, связанный с нашим сетевым интерфейсом en0.

$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

Примечание. Мне предложили заменить grep inet | grep -v inet6 такой надёжной командой awk:

$ ifconfig en0 | awk ' $1 == "inet" { print $2 }'
192.168.0.38

Она короче и конкретно нацелена на IPv4 с условием $1 == "inet".

Извлечение значения из файла конфигурации

$ grep 'editor =' ~/.gitconfig  | cut -d = -f2 | sed 's/ //g'
/usr/bin/vim

В файле конфигурации git текущего пользователя ищем значение editor =, обрезаем знак =, извлекаем второй столбец и удаляем все пробелы вокруг.

$ grep 'editor =' ~/.gitconfig
     editor = /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2
 /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2 | sed 's/ //'
/usr/bin/vim

Извлечение IP-адресов из файла журнала

Следующий реальный код ищет в журнале БД сообщение Too many connections from (за ним следует IP-адрес) и отображает десять главных нарушителей.

$ grep 'Too many connections from' db.log | 
  awk '{ print $12 }' | 
  sed 's@/@@' | 
  sort | 
  uniq -c | 
  sort -rn | 
  head -n 10 | 
  awk '{ print $2 }'
   10.11.112.108
   10.11.111.70
   10.11.97.57
   10.11.109.72
   10.11.116.156
   10.11.100.221
   10.11.96.242
   10.11.81.68
   10.11.99.112
   10.11.107.120

Давайте разберем, что делает этот конвейер. Во-первых, как выглядит строка в журнале.

$ grep "Too many connections from" db.log | head -n 1
2020-01-01 08:02:37,617 [myid:1] - WARN  [NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many connections from /10.11.112.108 - max is 60

Затем awk '{ print $12 }' извлекает из строки IP-адрес.

$ grep "Too many connections from" db.log | awk '{ print $12 }'
/10.11.112.108
...

Команда sed 's@/@@' удаляет начальный слэш.

$ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@'
10.11.112.108
...

Примечание. Как мы уже видели ранее, в sed можно использовать любой разделитель. Хотя обычно в качестве разделителя используется /, здесь мы заменяем именно этот символ, что слегка ухудшит читаемость выражения подстановки.

sed 's////'

sort | uniq -c сортирует IP-адреса в лексикографическом порядке, а затем удаляет дубликаты, добавляя перед IP-адресами количество вхождений каждого.

$ grep 'Too many connections from' db.log | 
  awk '{ print $12 }' | 
  sed 's@/@@' | 
  sort | 
  uniq -c
   1379 10.11.100.221
   1213 10.11.103.168
   1138 10.11.105.177
    946 10.11.106.213
   1211 10.11.106.4
   1326 10.11.107.120
   ...

sort -rn | head -n 10 сортирует строки по количеству вхождений, численно и в обратном порядке, чтобы главные нарушители выводились в первую очередь, из которых отображаются 10 строк. Последняя команда awk { print $2 } извлекает сами IP-адреса.

$ grep 'Too many connections from' db.log | 
  awk '{ print $12 }' | 
  sed 's@/@@' | 
  sort | 
  uniq -c | 
  sort -rn | 
  head -n 10 | 
  awk '{ print $2 }'
  10.11.112.108
  10.11.111.70
  10.11.97.57
  10.11.109.72
  10.11.116.156
  10.11.100.221
  10.11.96.242
  10.11.81.68
  10.11.99.112
  10.11.107.120

Переименование функции в исходном файле

Представим, что мы работаем над проектом и хотели бы переименовать недачно названную функцию (или класс, переменную и т. д.) в исходном файле. Можно сделать это с помощью команды sed -i, которая выполняет замену прямо в оригинальном файле.

$ cat izk/utils.py
def bool_from_str(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

$ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py
$ cat izk/utils.py
def is_affirmative(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

Примечание. На macOS вместо sed -i используйте sed -i ''.

Однако мы переименовали функцию только в оригинальном файле. Это сломает импорт bool_from_str в любом другом файле, поскольку эта функция больше не определена. Нужно найти способ переименовать bool_from_str повсюду в нашем проекте. Такого можно добиться с помощью команд grep, sed, а также циклов for или с помощью xargs.

Углубляемся: циклы for и xargs

Чтобы заменить в нашем проекте все вхождения bool_from_str, сначала нужно рекурсивно найти их с помощью grep -r.

$ grep -r bool_from_str .
./tests/test_utils.py:from izk.utils import bool_from_str
./tests/test_utils.py:def test_bool_from_str(s, expected):
./tests/test_utils.py:    assert bool_from_str(s) == expected
./izk/utils.py:def bool_from_str(s):
./izk/prompt.py:from .utils import bool_from_str
./izk/prompt.py:                    default = bool_from_str(os.environ[envvar])

Поскольку нас интересуют только файлы c совпадениями, также необходимо использовать опцию -l/--files-with-matches:

-l, --files-with-matches
        Only the names of files containing selected lines are written to standard out-
        put.  grep will only search a file until a match has been found, making
        searches potentially less expensive.  Pathnames are listed once per file
        searched.  If the standard input is searched, the string ``(standard input)''
        is written.

$ grep -r --files-with-matches bool_from_str .
./tests/test_utils.py
./izk/utils.py
./izk/prompt.py

Затем можем использовать команду xargs для осуществления действий с каждой строки выходных данных (то есть всех файлов, содержащих строку bool_from_str).

$ grep -r --files-with-matches bool_from_str . | 
  xargs -n 1 sed -i 's/bool_from_str/is_affirmative/'

Опция -n 1 указывает, что каждая строка в выходных данных должна выполнить отдельную команду sed.

Затем выполняются следующие команды:

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

Если команда, которую вы вызываете с помощью xargs (в нашем случае sed), поддерживает несколько аргументов, то следует отбросить аргумент -n 1 для производительности.

grep -r --files-with-matches bool_from_str . | xargs sed -i 's/bool_from_str/is_affirmative/'

Эта команда затем исполнит

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py

Примечание. Из синопсиса sed на ман-странице видно, что команда может принять несколько аргументов.

SYNOPSIS
     sed [-Ealn] command [file ...]
     sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]

Действительно, как мы видели в предыдущей главе, file ... означает, что принимаются несколько аргументов, представляющих собой имена файлов.

Мы видим, что произведены замены для всех вхождений bool_from_str.

$ grep -r is_affirmative .
./tests/test_utils.py:from izk.utils import is_affirmative
./tests/test_utils.py:def test_is_affirmative(s, expected):
./tests/test_utils.py:    assert is_affirmative(s) == expected
./izk/utils.py:def is_affirmative(s):
./izk/prompt.py:from .utils import is_affirmative
./izk/prompt.py:                    default = is_affirmative(os.environ[envvar])

Как это часто бывает, существует несколько способов достижения одного и того же результата. Вместо xargs мы могли бы использовать циклы for, чтобы перебирать строки по списку и выполнять действие над каждым элементом. У этих циклов такой синтаксис:

for item in list; do
    command $item
done

Если обернуть нашу команду grep в $(), то оболочка выполнит её в подоболочке, результат чего затем будет повторён в цикле for.

$ for file in $(grep -r --files-with-matches bool_from_str .); do
  sed -i 's/bool_from_str/is_affirmative/' $file
done

Эта команда выполнит

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

Синтаксис циклов for кажется мне более чётким, чем у xargs, однако последняя может выполнять команды параллельно, используя параметры -P n, где n — максимальное количество параллельных команд, выполняемых одновременно, что может дать выигрыш в производительности.

Резюме

Все эти инструменты открывают целый мир возможностей, так как позволяют извлекать и преобразовывать данные, создавая целые конвейеры из команд, которые, возможно, никогда не предназначались для совместной работы. Каждая из них выполняет относительно небольшую функцию (сортировка sort, объединение cat, фильтры grep, редактирование sed, вырезание cut и т. д.).

Любую задачу, включающую текст, можно свести к конвейеру более мелких задач, каждая из которых выполняет простое действие и передаёт свои выходные данные в следующую задачу.

Например, если нам хочется узнать, сколько уникальных IP-адресов в файле журнала, и чтобы эти IP-адреса всегда появлялись в одном и том же столбце, то можно запустить следующую последовательность команд:

  • grep строк, которые соответствуют шаблону строк с IP-адресами
  • найти столбец с IP-адресом, извлечь его с помощью awk
  • отсортировать список IP-адресов с помощью sort
  • устранить смежные дубликаты с помощью uniq
  • подсчитать количество строк (то есть уникальных IP-адресов) с помощью wc -l

Поскольку есть множество нативных и сторонних инструментов обработки текста, также много способов решить любую задачу.

Примеры в этой статье были надуманными, но я предлагаю вам прочитать удивительную статью «Инструменты командной строки могут быть в 235 раз быстрее, чем ваш кластер Hadoop», чтобы получить представление о том, насколько полезны и мощны эти команды на самом деле и какие реальные проблемы они могут решить.

Что дальше

  1. Подсчитайте количество файлов и каталогов, расположенных в вашем домашнем каталоге.
  2. Отобразите содержимое файла только прописными буквами.
  3. Подсчитайте, сколько раз встречалось каждое слово в файле.
  4. Подсчитайте количество гласных в файле. Отсортируйте результат от наиболее распространённой до наименее распространённой буквы.


Будущая книга «Основные инструменты и практики для начинающего разработчика программного обеспечения» (Essential Tools and Practices for the Aspiring Software Developer) Бальтазара Рубероля и Этьена Броду поможет создать продуктивную среду разработки и познакомиться с полезными инструментами и практиками, которые нужны для профессионального роста. Как уже было сказано, она охватит такие темы, как освоение терминала, настройка и эффективная работа в командной оболочке, управление версиями кода с помощью git, основы SQL, инструменты вроде Make, jq и регулярные выражения, основы сетевого взаимодействия, а также лучшие практики разработки программного обеспечения и совместной работы.

Если интересно поучаствовать в проекте, подписывайтесь на список рассылки!

Делайте так:

grep -rnw '/путь/к/папке/с/файлами' -e "шаблон"
  • -r или -R — рекурсивный поиск.
  • -n — вывод номера строки.
  • -w — только целые слова.
  • -l (нижний регистр от L) — вывод имени файла, где было совпадение.

Эффективности добавят следующие флаги:

  • --exclude — Шаблон для исключения файлов, например: поиск везде, кроме файлов с расширением .o:

     grep --exclude=*.o -rnw '/путь/к/папке/с/файлами' -e "шаблон"
    
  • --include — Поиск только в определённых файлах, например: только в файлах с расширениями .h и .c:

     grep --include=*.{c,h} -rnw '/путь/к/папке/с/файлами' -e "шаблон"
    
  • --exclude-dir и --include-dir — то же, только для выборки директорий, например: исключить папки один, two и любые, начинающиеся на а:

     grep --exclude-dir={один,two,а*} -rnw '/путь/к/папке/с/файлами' -e "шаблон"
    

Это отлично работает для подобных целей, остальные настройки для grep можно узнать, выполнив man grep.


При формировании ответа использовался ответ на вопрос «How do I find all files containing specific text on Linux?» от участника @rakib.

По умолчанию большинство инструментов поиска смотрят имена файлов, а не их содержимое.

Тем не менее, самая известная программа поиска GNU, grep, будет искать файлы с правильными флагами.

Здесь мы покажем вам, как вы можете найти конкретные слова в файле в Linux.

Использование grep для поиска определенного слова в файле

По умолчанию grep просматривает содержимое файлов, а также их имена.

Он включен в большинство систем Linux и в целом одинаков для всех дистрибутивов.

Тем не менее, небольшие или менее мощные Linux-боксы могут предпочесть выполнить другую команду, например, ack.

В зависимости от того, как файл закодирован, grep не всегда может заглянуть внутрь.

Но для большинства текстовых форматов grep может сканировать текст файла по указанному шаблону.

grep -Rw '/path/to/search/' -e 'pattern'

Флаг -R устанавливает grep в рекурсивный режим, перемещаясь по всем каталогам, содержащимся в указанном каталоге.

Флаг -w ищет совпадения всего слова.

Это означает, что «red» будет соответствовать только «red», окруженному пробельными символами, а не «redundant» или «tired».

Флаг -e предшествует шаблону для поиска.

Он поддерживает регулярные выражения по умолчанию.

Чтобы ускорить grep, вы можете использовать флаги –exclude и –include, чтобы ограничить поиск определенными типами файлов.

Например, –exclude = *. Csv не будет искать в файлах с расширением .csv. –include = *. txt, с другой стороны, будет выполнять поиск только в файлах с расширением .txt.

Флаг можно добавить сразу после команды grep, как показано ниже:

grep --exclude=*.csv -Rw '/path/to/search' -e 'pattern'

Вы также можете исключить указанные каталоги, следуя приведенному ниже формату:

grep --exclude-dir={dir1,dir2,*_old} -Rw '/path/to/search' -e 'pattern'

Эта команда не будет выполнять поиск в каких-либо каталогах в текущем рабочем каталоге с именами dir1, dir2 или с шаблоном * _old, исключая их из процесса поиска.

Он выполнит указанный рекурсивный поиск по полному слову во всех других файлах в текущем рабочем каталоге.

Использование find для поиска определенного слова в файле

Хотя синтаксис команды find является более сложным, чем grep, некоторые предпочитают его.

find . -name "*.php" -exec grep "pattern" {} ;

Эта команда будет использовать флаг find -exec для передачи найденных файлов в grep для поиска.

Благодаря продуманному синтаксису вы можете использовать более быстрый поиск в файловой системе c find, чтобы найти конкретные типы файлов, в которых вы хотите искать, и затем направить их в grep для поиска внутри файлов.

Обратите внимание, что команда find ищет только имена файлов, а не их содержимое.

Вот почему grep требуется для поиска текста и содержимого файла.

Обычные флаги grep должны быть полностью работоспособны в пределах флага -exec.

Использование ack для поиска определенного слова в файле

Команда ack, вероятно, является самым быстрым инструментом поиска, но она не так популярна, как описанные выше опции.

Команда, показанные ниже будет искать в текущем каталоге.

ack 'pattern'

Если вы хотите выполнить поиск в определенном файле или каталоге, вы можете добавить этот файл или полный путь к вашему поиску.

ack 'pattern' /path/to/file.txt

Заключение

Для большинства людей в большинстве ситуаций, grep – лучший широко доступный инструмент поиска.

Вы также можете посмотреть другие инструменты поиска, такие как RipGrep, для более быстрого поиска.

Понравилась статья? Поделить с друзьями:
  • Как составить меню для сайта
  • Как по градусам найти точку на карте
  • Как найти династию по фамилии
  • Как найти рассказ который читала сегодня яндекс
  • Как найти девушку подкаст