Иногда может понадобится найти файл, в котором содержится определённая строка или найти строку в файле, где есть нужное слово. В 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 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 expression
grep -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», чтобы получить представление о том, насколько полезны и мощны эти команды на самом деле и какие реальные проблемы они могут решить.
Что дальше
- Подсчитайте количество файлов и каталогов, расположенных в вашем домашнем каталоге.
- Отобразите содержимое файла только прописными буквами.
- Подсчитайте, сколько раз встречалось каждое слово в файле.
- Подсчитайте количество гласных в файле. Отсортируйте результат от наиболее распространённой до наименее распространённой буквы.
Будущая книга «Основные инструменты и практики для начинающего разработчика программного обеспечения» (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, для более быстрого поиска.