Как использовать команду grep в GNU / Linux

Введение Grep - мощный, но очень простой инструмент. По умолчанию он просматривает входные данные и печатает одну или несколько строк, содержащих текст, соответствующий шаблону, указанному в вызове команды. Прежде чем grep стал таким широко распространенным инструментом для системы GNU / Linux, это была частная утилита, написанная Кеном Томпсоном [https://en.wikipedia.org/wiki/Ken_Thompson] для поиска в файлах. Интересная часть истории заключается в том, что к нему подошел его менеджер и попросил инструмент, который

Вступление

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

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

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

В этой статье мы изучим основы grep и его использование, просмотрев его параметры и несколько примеров.

Использование и варианты Grep

Общая форма команды grep :

 $ grep [OPTION...] [PATTERNS] [FILE...] 

Мы можем указать ноль или несколько аргументов OPTION , один или несколько ШАБЛОНОВ и ноль или несколько аргументов FILE. Если аргумент FILE не указан, grep просматривает рабочий каталог ( . ), Если задана опция рекурсии; в противном случае grep ищет стандартный входной канал.

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

  • -G или --basic-regexp - при использовании интерпретирует шаблон как базовое регулярное выражение (BRE). Этот вариант используется по умолчанию (если не указаны другие параметры).
  • -E или --extended-regexp - интерпретирует шаблон как расширенное регулярное выражение (ERE).
  • -F или --fixed-strings - интерпретирует шаблон как фиксированные строки, а не как регулярные выражения.
  • -P или --perl-regexp - интерпретирует шаблон как Perl-совместимые регулярные выражения (PCRE). Этот вариант по-прежнему имеет некоторые нереализованные функции и может выдавать предупреждения. Его следует считать скорее экспериментальным при использовании с определенными параметрами и рекомендуется только для опытных пользователей.

Стоит отметить, что в настоящее время grep - это семейство инструментов, в которое входят egrep , fgrep и rgrep . Они такие же, как grep -E , grep -F и grep -R соответственно, но устарели как автономные инструменты и предоставляются только потому, что некоторые программы по-прежнему полагаются на них.

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

Поиск в файле

Скажем, у нас есть test.txt со следующим содержимым:

 hello 
 hElLo 
 This line does not include the word we're looking for. 
 helloHello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Test line. 
 Another hello line. 

Мы хотим найти все строки, содержащие слово hello . Не имеет значения, где находится слово в строке, и не имеет значения, является ли оно частью более длинного слова, такого как helloHello .

Мы будем использовать grep -E с шаблоном hello в файле test.txt

 $ grep -E hello test.txt 

Выполнение этой команды даст:

 hello 
 helloHello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Another hello line. 

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

Поиск нескольких слов

Иногда нам нужно найти пару слов вместо одного. Это делается путем простого включения условий поиска в кавычки:

 $ grep -E "This is" test.txt 

Выполнение этого приведет к:

 This is the paragraph that has multiple sentences. We'll put one more hello here. 

Имейте в виду, что grep чувствителен к регистру. Если бы мы "this is" , ничего бы не вернулось.

Использование регулярных выражений

Теперь давайте воспользуемся регулярным выражением, чтобы выделить слово hello и пропустить такие результаты, как helloHello :

 $ grep -E "(\s|[^a-zA-Z0-9_]*)hello\s" test.txt 

Выполнение этой команды приведет к:

 hello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Another hello line. 

Указание правил сопоставления с помощью флагов

Есть несколько опций, которые помогают нам легче указать правила сопоставления:

  • -e шаблон

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

Например, мы можем искать несколько шаблонов, например:

 $ grep -E -e "hello" -e "the" test.txt 

Эта команда приведет к:

 hello 
 This line does not include the word we're looking for. 
 helloHello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Another hello line. 
  • -f файл

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

 $ grep -E -f patterns.txt test.txt 

Вот так выглядит наш файл patterns.txt

 ^helloHello$ 
 *line* 

Выполнение команды приведет к:

 This line does not include the word we're looking for. 
 helloHello 
 Test line. 
 Another hello line. 
  • -i или --ignore-case

Этот флаг отменяет поведение по умолчанию с учетом регистра и возвращает все совпадающие шаблоны, независимо от регистра:

 $ grep -E -i "hello" test.txt 

Выполнение этой команды приведет к:

 hello 
 hElLo 
 helloHello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Another hello line. 

На этот раз, даже несмотря на то, что наш поисковый helLo "hello" , были возвращены другие соответствующие строки, такие как helLo.

Примечание: -y - устаревшая версия -i которая делает то же самое, но сохраняется только для обратной совместимости.

  • -v или --invert-match

Инвертирует совпадение - возвращает строки, не соответствующие нашему шаблону:

 $ grep -E -v "hello" test.txt 

Выполнение этой команды приведет к:

 hElLo 
 This line does not include the word we're looking for. 
 Test line. 
  • -w или --word-regexp

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

 $ grep -E -w "hello" test.txt 

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

 hello 
 This is the paragraph that has multiple sentences. We'll put one more hello here. 
 Another hello line. 
  • -x или --line-regexp

Ищет строки, соответствующие всему шаблону. То есть, если шаблон и вся строка совпадают, возвращается строка:

 $ grep -E -x "helloHello" test.txt 

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

 $ grep -E "^helloHello$" test.txt 

У них будет тот же результат - в нашем случае одна строка, содержащая именно то, что мы указали:

 helloHello 

Контроль вывода

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

  • -c или --count

Подавляет вывод и возвращает количество совпавших строк:

 $ grep -E -c "hello" test.txt 

Эта команда приведет к единственному числу:

 4 
  • -l

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

 $ grep -E -l "hello" *.txt 

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

Эта команда приведет к следующему:

 01_contains_hello.txt 
 02_contains_hello.txt 
 test.txt 

Напротив, опция -L показывает имена файлов, которые не имеют соответствий.

  • -m число

Остановка поиска после num найденных строк. При поиске в нескольких файлах это число не переносится между файлами, и счет начинается заново для каждого из них:

 $ grep -E -m 2 "hello" test.txt 

Эта команда приведет к следующему:

 hello 
 helloHello 
  • -час

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

 $ grep -E -m 1 "hello" *.txt 

Это приводит к:

 01_contains_hello.txt:hello 
 02_contains_hello.txt:hello 
 test.txt:hello 

Однако, когда мы добавляем флаг -h

 $ grep -E -h -m 1 "hello" *.txt 

Результат:

 hello 
 hello 
 hello 
  • -n

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

 $ grep -E -n "hello" test.txt 

Это приводит к:

 1:hello 
 4:helloHello 
 5: This is the paragraph that has multiple sentences. We'll put one more hello here. 
 7:Another hello line. 

Практическое использование

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

Поиск по расширению файла с помощью Grep

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

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

Здесь мы будем использовать grep для фильтрации вывода другого инструмента, который рекурсивно перечисляет все файлы в текущем рабочем каталоге. Мы попытаемся найти все файлы с .txt и отсортировать их по алфавиту, не обращая внимания на регистр:

 $ find . | sort -f | grep -E "*.txt" 

Обратите внимание, что в этой команде мы использовали | , или так называемая труба . Мы использовали его для перенаправления вывода одной программы на ввод другой, вместо того, чтобы выводить его на стандартный вывод, как мы обычно это делаем.

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

 ./some_file1.txt 
 ./05_contains_hello.txt 
 ./subfolder 
 ./subfolder/py_code.py 
 ./subfolder/some_file.txt 
 ./c_code.c 
 ./markdown_file.md 
 ./some_file2.txt 

Результат нашей команды будет:

 ./05_contains_hello.txt 
 ./some_file1.txt 
 ./some_file2.txt 
 ./subfolder/some_file.txt 

Обработка содержимого файла с помощью Grep

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

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

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

 $ grep -E -rn "osCreateMemoryBlock\([^)]*(\s*|\))" 

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

Здесь мы объединили две опции - -r и -n в одну -rn . Это потому, что мы хотим знать, в какие строки необходимо внести изменения.

Вот как выглядит рабочий каталог для этой команды:

 ./subfolder2 
 ./subfolder2/c_code.c 
 ./subfolder2/memory_admin.c 
 ./log_client.c 
 ./signals.c 
 ./log_server.c 
 ./fifo_server.c 
 ./fifo_client.c 
 ./skelet.c 
 ./subfolder1 
 ./subfolder1/memory_handler.c 
 ./subfolder1/random_code1.c 
 ./subfolder1/c_code.c 
 ./subfolder1/memory_creater.c 
 ./shared_memory_writer.c 
 ./shared_memory_reader.c 

Выполнение команды выведет относительный путь к каждому файлу и номер каждой строки, содержащей osCreateMemoryBlock :

 subfolder2/c_code.c:47:void *osCreateMemoryBlock(const char* filePath, unsigned size); 
 subfolder2/c_code.c:116:void *osCreateMemoryBlock(const char* filePath, 
 subfolder2/memory_admin.c:54: osMemoryBlock* pMsgBuf = osCreateMemoryBlock(argv[1], sizeof(osMemoryBlock)); 
 subfolder2/memory_admin.c:116:void *osCreateMemoryBlock(const char* filePath, 
 log_server.c:116:void *osCreateMemoryBlock(const char* filePath, 
 subfolder1/memory_handler.c:54: osMemoryBlock* pMsgBuf = osCreateMemoryBlock(argv[1], sizeof(osMemoryBlock)); 
 subfolder1/memory_handler.c:116:void *osCreateMemoryBlock(const char* filePath, 
 subfolder1/memory_creater.c:47:void *osCreateMemoryBlock(const char* filePath, unsigned size); 
 subfolder1/memory_creater.c:54: osMemoryBlock* pMsgBuf = osCreateMemoryBlock(argv[1], sizeof(osMemoryBlock)); 
 shared_memory_writer.c:33: int *niz = osCreateMemoryBlock(argv[1], size); 

Извлечение данных файла журнала с помощью Grep

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

 $ grep -E -n "May 27 19:2[0-1]:[0-9]{2} *" /var/log/syslog 

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

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

 1005:May 27 19:21:59 machine kernel: [236041.840122] mce: CPU0: Core temperature above threshold, cpu clock throttled (total events = 2918) 
 1006:May 27 19:21:59 machine kernel: [236041.840125] mce: CPU0: Package temperature above threshold, cpu clock throttled (total events = 3753) 
 1014:May 27 19:21:59 machine kernel: [236041.841137] mce: CPU4: Core temperature/speed normal 
 1016:May 27 19:21:59 machine kernel: [236041.841139] mce: CPU4: Package temperature/speed normal 

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

Заключение

В этой статье мы рассмотрели основы одного из самых известных доступных инструментов командной строки. Это очень эффективный способ поиска в любом тексте или извлечения из него данных.

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

comments powered by Disqus