Что такое grep и с чем его едят

Эта заметка навеяна мелькавшими последнее время на хабре постами двух тематик — «интересные команды unix» и «как я подбирал программиста». И описываемые там команды, конечно, местами интересные, но редко практически полезные, а выясняется, что реально полезным инструментарием мы пользоваться и не умеем.Небольшое лирическое отступление: Года три назад меня попросили провести собеседование с претендентами на должность unix-сисадмина. На двух крупнейших на тот момент фриланс-биржах на вакансию откликнулись восемь претендентов, двое из которых входили в ТОП-5 рейтинга этих бирж. Я никогда не требую от админов знания наизусть конфигов и считаю, что нужный софт всегда освоится, если есть желание читать, логика в действиях и умение правильно пользоваться инструментарием системы. Посему для начала претендентам было даны две задачки, примерно такого плана: — поместить задание в крон, которое будет выполняться в каждый чётный час и в 3 часа; — распечатать из файла /var/run/dmesg.boot информацию о процессоре.К моему удивлению никто из претендентов с обеими вопросами не справился. Двое, в принципе, не знали о существовании grep.imageПоэтому… Лето… Пятница… Перед шашлыками немного поговорим о grep.Зная местную публику и дабы не возникало излишних инсинуаций сообщаю, что всё нижеизложенное справедливо для # grep --version | grep grep grep (GNU grep) 2.5.1-FreeBSD Это важно в связи с # man grep | grep -iB 2 freebsd -P, --perl-regexp Interpret PATTERN as a Perl regular expression. This option is not supported in FreeBSD. Для начала о том как мы обычно grep’аем файлы.Используя cat:

root@nm3:/ # cat /var/run/dmesg.boot | grep CPU: CPU: Intel® Core2 Quad CPU Q9550 @ 2.83GHz (2833.07-MHz K8-class CPU) Но зачем? Ведь можно и так: root@nm3:/ # grep CPU: /var/run/dmesg.boot CPU: Intel® Core2 Quad CPU Q9550 @ 2.83GHz (2833.07-MHz K8-class CPU) Или вот так (ненавижу такую конструкцию): root@nm3:/ #

sixteen seventeen eighteen seven sixteen seventeen eighteen twenty seven one 504 one one 503 one one 504 one one 504 one #comment UP twentyseven #comment down twenty1 twenty3 twenty5 twenty7

И приступим к поискам: Опция -w позволяет искать по слову целиком: root@nm3:/ # grep -w 'seven' test.txt seven eight one eight three sixteen seventeen eighteen seven twenty seven А если нужно по началу или концу слова? root@nm3:/ # grep '\' test.txt seven eight one eight three sixteen seventeen eighteen seven twenty seven twentyseven Стоящие в начале или конце строки? root@nm3:/ # grep '^seven' test.txt seven eight one eight three root@nm3:/ # grep 'seven$' test.txt sixteen seventeen eighteen seven twenty seven twentyseven root@nm3:/ # Хотите увидеть строки в в окрестности искомой? root@nm3:/ # grep -C 1 twentyseven test.txt #comment UP twentyseven #comment down Только снизу или сверху? root@nm3:/ # grep -A 1 twentyseven test.txt twentyseven #comment down root@nm3:/ # grep -B 1 twentyseven test.txt #comment UP twentyseven А ещё мы умеем так root@nm3:/ # grep «twenty[1–4]» test.txt twenty1 twenty3 И наоборот исключая эти root@nm3:/ # grep «twenty[^1–4]» test.txt twenty seven twentyseven twenty5 twenty7 Разумеется grep поддерживает и прочие базовые квантификаторы, метасимволы и другие прелести регулярокПару практических примеров: root@nm3:/ # cat /etc/resolv.conf #options edns0 #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4 Отбираем только строки с ip: root@nm3:/ # grep -E »[0–9]{1,3}\.[0–9]{1,3}\.[0–9]{1,3}\.[0–9]{1,3}» /etc/resolv.conf #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4 Работает, но так симпатичнее: root@nm3:/ # grep -E '\b[0–9]{1,3}(\.[0–9]{1,3}){3}\b' /etc/resolv.conf #nameserver 127.0.0.1 nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4 Уберём строку с комментарием? root@nm3:/ # grep -E '\b[0–9]{1,3}(\.[0–9]{1,3}){3}\b' /etc/resolv.conf | grep -v # nameserver 8.8.8.8 nameserver 77.88.8.8 nameserver 8.8.4.4 А теперь выберем только сами ip root@nm3:/ # grep -oE '\b[0–9]{1,3}(\.[0–9]{1,3}){3}\b' /etc/resolv.conf | grep -v # 127.0.0.1 8.8.8.8 77.88.8.8 8.8.4.4 Вот незадача… Закомментированная строка вернулась. Это связано с особенностью обработки шаблонов. Как быть? Вот так: root@nm3:/ # grep -v # /etc/resolv.conf | grep -oE '\b[0–9]{1,3}(\.[0–9]{1,3}){3}\b' 8.8.8.8 77.88.8.8 8.8.4.4 Здесь остановимся на инвертировании поиска ключом -vДопустим нам нужно выполнить «ps -afx | grep ttyv» root@nm3:/ # ps -afx | grep ttyv 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7 48798 2 S+ 0:00.00 grep ttyv Всё бы ничего, но строка »48798 2 S+ 0:00.00 grep ttyv» нам не нужна. Используем -v root@nm3:/ # ps -afx | grep ttyv | grep -v grep 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7 Некрасивая конструкция? Потрюкачим немного: root@nm3:/ # ps -afx | grep »[t]tyv» 1269 v1 Is+ 0:00.00 /usr/libexec/getty Pc ttyv1 1270 v2 Is+ 0:00.00 /usr/libexec/getty Pc ttyv2 1271 v3 Is+ 0:00.00 /usr/libexec/getty Pc ttyv3 1272 v4 Is+ 0:00.00 /usr/libexec/getty Pc ttyv4 1273 v5 Is+ 0:00.00 /usr/libexec/getty Pc ttyv5 1274 v6 Is+ 0:00.00 /usr/libexec/getty Pc ttyv6 1275 v7 Is+ 0:00.00 /usr/libexec/getty Pc ttyv7 Также не забываем про | (ИЛИ) root@nm3:/ # vmstat -z | grep -E »(sock|ITEM)» ITEM SIZE LIMIT USED FREE REQ FAIL SLEEP socket: 696, 130295, 30, 65, 43764, 0, 0 ну и тоже самое, иначе: root@nm3:/ # vmstat -z | grep «sock\|ITEM» ITEM SIZE LIMIT USED FREE REQ FAIL SLEEP socket: 696, 130295, 30, 65, 43825, 0, 0 Ну и если о использовании регулярок в grep’e помнят многие, то об использовании POSIX классов как-то забывают, а это тоже иногда удобно.POSIX [: alpha:] Any alphabetical character, regardless of case[: digit:] Any numerical character[: alnum:] Any alphabetical or numerical character[: blank:] Space or tab characters[: xdigit:] Hexadecimal characters; any number or A–F or a–f[: punct:] Any punctuation symbol[: print:] Any printable character (not control characters)[: space:] Any whitespace character[: graph:] Exclude whitespace characters[: upper:] Any uppercase letter[: lower:] Any lowercase letter[: cntrl:] Control characters

Отберём строки с заглавными символами: root@nm3:/ # grep »[[: upper:]]» test.txt #comment UP Плохо видно что нашли? Подсветим: imageНу и ещё пару трюков для затравки.Первый скорее академичный. За лет 15 ни разу его не использовал: Нужно из нашего тестового файла выбрать строки содержащие six или seven или eight: Пока всё просто:

root@nm3:/ # grep -E »(six|seven|eight)» test.txt seven eight one eight three sixteen seventeen eighteen seven sixteen seventeen eighteen twenty seven twentyseven А теперь только те строки в которых six или seven или eight встречаются несколько раз. Эта фишка именуется Backreferences root@nm3:/ # grep -E »(six|seven|eight).*\1» test.txt seven eight one eight three sixteen seventeen eighteen seven Ну и второй трюк, куда более полезный. Необходимо вывести строки в которых 504 с обеих сторон ограничено табуляцией.Ох как тут не хватает поддержки PCRE…Использование POSIX-классов не спасает: root@nm3:/ # grep »[[: blank:]]504[[: blank:]]» test.txt one 504 one one 504 one one 504 one На помощь приходит конструкция [CTRL+V][TAB]: root@nm3:/ # grep » 504 » test.txt one 504 one Что ещё не сказал? Разумеется, grep умеет искать в файлах/каталогах и, разумеется, рекурсивно. Найдём в исходниках код, где разрешается использование Intel’ом сторонних SFP-шек. Как пишется allow_unsupported_sfp или unsupported_allow_sfp не помню. Ну да и ладно — это проблемы grep’а: root@nm3:/ # grep -rni allow /usr/src/sys/dev/ | grep unsupp /usr/src/sys/dev/ixgbe/README:75: of unsupported modules by setting the static variable 'allow_unsupported_sfp' /usr/src/sys/dev/ixgbe/ixgbe.c:322: static int allow_unsupported_sfp = TRUE; /usr/src/sys/dev/ixgbe/ixgbe.c:323: TUNABLE_INT («hw.ixgbe.unsupported_sfp», &allow_unsupported_sfp); /usr/src/sys/dev/ixgbe/ixgbe.c:542: hw→allow_unsupported_sfp = allow_unsupported_sfp; /usr/src/sys/dev/ixgbe/ixgbe_type.h:3249: bool allow_unsupported_sfp; /usr/src/sys/dev/ixgbe/ixgbe_phy.c:1228: if (hw→allow_unsupported_sfp == TRUE) { Надеюсь не утомил. И это была только вершина айсберга grep. Приятного Вам чтения, а мне аппетита на шашлыках! Ну и удачного Вам grep’a!

© Habrahabr.ru