Ищу работу админа в Минске(открыт для предложений)
45,1
рейтинг
11 июля 2014 в 16:40

Администрирование → Что такое 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(R) Core(TM)2 Quad CPU    Q9550  @ 2.83GHz (2833.07-MHz K8-class CPU)

Но зачем? Ведь можно и так:
root@nm3:/ # grep CPU: /var/run/dmesg.boot
CPU: Intel(R) Core(TM)2 Quad CPU    Q9550  @ 2.83GHz (2833.07-MHz K8-class CPU)

Или вот так (ненавижу такую конструкцию):
root@nm3:/ # </var/run/dmesg.boot grep CPU:
CPU: Intel(R) Core(TM)2 Quad CPU    Q9550  @ 2.83GHz (2833.07-MHz K8-class CPU)

Зачем-то считаем отобранные строки с помощью wc:
root@nm3:/ # grep WARNING /var/run/dmesg.boot | wc -l
       3

Хотя можно:
root@nm3:/ # grep WARNING /var/run/dmesg.boot -c
3

Сделаем тестовый файлик:
test.txt
root@nm3:/ # grep ".*" test.txt
one two three
seven eight one eight three
thirteen fourteen fifteen

 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 '\<seven' test.txt
seven eight one eight three
 sixteen seventeen eighteen seven
sixteen seventeen eighteen
        twenty seven
root@nm3:/ # grep 'seven\>' 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!
Alexander @simpleadmin
карма
101,0
рейтинг 45,1
Ищу работу админа в Минске(открыт для предложений)
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Администрирование

Комментарии (143)

  • +2
    Еще можно добавить про расширения grep —

    grep is used for simple patterns and basic regular expressions (BREs); egrep can handle extended regular expres-
    sions (EREs). See re_format(7) for more information on regular expressions. fgrep is quicker than both grep
    and egrep, but can only handle fixed patterns (i.e. it does not interpret regular expressions). Patterns may
    consist of one or more lines, allowing any of the pattern lines to match a portion of the input.

    zgrep, zegrep, and zfgrep act like grep, egrep, and fgrep, respectively, but accept input files compressed with
    the compress(1) or gzip(1) compression utilities.
  • +8
    Спасибо за пост! Действительно, отчего-то многие по каким-то непонятным причинам боятся осваивать grep. Поэтому простые вводные мануалы очень кстати. Вот ещё несколько кратких статеек о самых основах:
    Команда grep — человеческий man
    О grep для начинающих
    A Beginner’s Guide to Grep: Basics and Regular Expressions
    Beginner’s Guide to Grep
  • +4
    Подсветка синтаксиса такая подсветка. Ввод команды шелла раскрашен серым как комментарий. Даже не сразу сообразил, что за фигня.
    • +3
      Да, тяжело читать. Лучше совсем без подсветки, чем с такой
    • +3
      Просто не надо такие примеры из под рута запускать :-)
    • +1
      grep это ещё цветочки перед awk.
  • +1
    еще grep`ом удобно пустые строки с файла убирать

    >cat file
    1

    2
    3
    >egrep -v ^$ file
    1
    2
    3

    • 0
      grep. file
      • +1
        Интересный вариант, спасибо, не знал
        • +1
          Что тут знать-то? Вы же понимаете, что тут значит точка? Это не нужно запоминать, надо просто понимать как это работает.
          • +5
            Отсутствие пробела в примере между grep и точкой сбивает с толку.
            • +1
              Отсутствие пробела — это просто опечатка. Он таки нужен, иначе ничего не работает.
    • 0
      Ваш вариант хорош в комбинации с исключением комментариев. Например, демонстрировать конфиги удобно с помощью

      grep -Ev '^$|^\s*#' /etc/nginx/nginx.conf
      • –1
        grep ^[^#] /etc/nginx/nginx.conf (если перед # ничего нет)
        • 0
          Не убирает пустые строки, и вот лично у меня /etc/nginx/nginx.conf (как и в официальном примере) имеет пробельные символы перед комментариями.
  • +2
    Так?
    0 */2,3 * * *

    А ноль часов это четный час?
    • +18
      А ноль часов это четный час?

      Ну если делится без остатка на 2 то чётный )
  • 0
    Один из полезнейших и регуляршнейших инструментов. Не rocket science, но очень многим полезно было бы знать — не только системным администраторам.
  • +1
    Забыли упомянуть важную особенность грепа да и любой команды:
    man grep | grep -A1 -- -P
           -P, --perl-regexp
                  Interpret PATTERN as a Perl regular expression.  This is  highly
                  experimental and grep -P may warn of unimplemented features.
    


    Любимый вопрос для собеседования: удалить файл с именем "-". Хотя как можно не знать grep… Хотя на фрилансе искали, это многое объясняет.

    Насчёт cat не всегда справедливо. Я часто делаю сначала cat… а потом просто добавляю в конец grep, если надо.
    • 0
      Любимый вопрос для собеседования: удалить файл с именем «-».
      Вот так: unlink -
      • +2
        На самом деле конкретно с файлами проще всего использовать "./filename". Работает в любых командах, какой бы кривой парсер командной строки там не использовался. А вот -- — это для примера выше, то есть когда нужен аргумент, начинающийся с минуса и не являющийся именем файла.
        • 0
          Я знаю для чего он. unlink позволяет ни с чем не заморачиваться — она просто удаляет, что дадут.
          • 0
            Даже он не сможет так просто удалить файлы "--help" и "--version"
            • 0
              Видимо от ОС зависит:
              bolk@bolknote ~$ touch — --help
              bolk@bolknote ~$ ls — --help
              --help
              bolk@bolknote ~$ unlink --help
              bolk@bolknote ~$ ls — --help
              ls: --help: No such file or directory
              • 0
                Скорее от версии unlink. У меня GNU coreutils 8.21
                На FreeBSD, о которой идет речь в статье, конечно всё может быть по-другому.
    • 0
      Любимый вопрос для собеседования: удалить файл с именем "-".

      Я что-то упускаю?

      valdikss@valaptop ~/test % l
      total 8.0K
      -rw-r--r--  1 valdikss users    0 Jul 12 02:02 -
      drwxr-xr-x  2 valdikss users 4.0K Jul 12 02:02 .
      drwx------ 92 valdikss users 4.0K Jul 12 02:02 ..
      valdikss@valaptop ~/test % rm -
      valdikss@valaptop ~/test % l
      total 8.0K
      drwxr-xr-x  2 valdikss users 4.0K Jul 12 02:02 .
      drwx------ 92 valdikss users 4.0K Jul 12 02:02 ..
      valdikss@valaptop ~/test %
      • +2
        Имелось ввиду, видимо:

        # touch -- "-1.txt"
        # rm -1.txt
        rm: illegal option -- 1
        usage: rm [-f | -i] [-dIPRrvW] file ...
               unlink file
        # rm -- "-1.txt"
        #
        
        
        • +3
          Свежие версии rm стали умнее в таких случаях.
          $ rm --version
          rm (GNU coreutils) 8.21
          # more lines
          $ rm -l.txt
          rm: invalid option — 'l'
          Try 'rm ./-l.txt' to remove the file ‘-l.txt’.
    • 0
      $ touch '"-"'
      $ rm -f '"-"'

      :-)
      • 0
        А ведь и правда :-)
  • +1
    Две любимые команды:

    поиск процессов
    ps aux | grep mysql

    поиск текста в файлах директории
    grep -rl 'some text' ./
    • +5
      pgrep
    • 0
      Можно и без грепа:
      ps -C mysql
  • –1
    Использование POSIX-классов не спасает:

    А использование \t?
    • +3
      Так просто \t вы туда не засунете, но можно использовать $'\t' — но это уже не grep, а bash.
      • –2
        А чего там сувать-то? Жмём Ctrl+V в консоли, потом Tab.
        • +1
          Вы статью-то читали?
          • –1
            Да. Почитайте мои коментарии к ней.
            • +1
              Тогда я не понял ваше сообщение совершенно…
              • 0
                Зачем сувать \t или использовать башизмы, если можно просто сам «tab», как символ вставить? Вот о чём мой комментарий.
                • 0
                  Читабельность. И копипастить удобнее.
                  • +1
                    Ээээ… читабельность?
  • НЛО прилетело и опубликовало эту надпись здесь
    • +4
      Здесь лучше подойдёт awk.
    • +6
      ant0.ru/sed1line.html
      Печатать часть файла между двумя регулярными выражениями (включительно):
      • sed -n '/Iowa/,/Montana/p' (регистро зависимый)

      В вашем случае как-то так: sed -n '/ERROR/,/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/p'
    • +3
      Если хочется ненормального программирования, то можно grep -A 9999 ERROR | grep -B 9999 2014-07-11
      • 0
        Это только если ошибка была одна.
    • 0
      olorin@stitch:~$ echo "sdlfkj ololosdkfjl   sdkjfzhigurda sd;flk;lk" | grep -oE 'ololo(.)*zhigurda'
      ololosdkfjl   sdkjfzhigurda
      olorin@stitch:~$
      
      • +1
        Это работает только в одной строке.
        • 0
          Почему это? Сам так логи разбирал.

          root@server:/# tail ./test 
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "Backtrace:"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "[1] : The\Core\Application::onShutdown()"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "[2] /home//core/rev20140516113043/Core/Application.php:960 The\Core\TemplateEngine\Template::__toString()"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "[3] /home//core/rev20140516113043/Core/TemplateEngine/Template.php:23 The\Core\TemplateEngine\TemplateAdapter::run([Long string(10622)] '<!DOCTYPE html>"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "<html lang="ru-RU">"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "<head>"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "    <meta charset="utf-8">"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "    <title><?=$this->makeEcho...,array([Long string(214)] array(),object The\Core\TemplateEngine\TemplateRawInput,array([Long string(129)] array([Long string(...))"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "[4] /home//core/rev20140516113043/Core/TemplateEngine/TemplateAdapter.php:30 eval()"
          [12-Jul-2014 10:43:38] WARNING: [pool admin] child 20811 said into stderr: "----------------------------------------------------------..."
          root@server:/# tail ./test | grep -oE "\"(.*)\""
          "Backtrace:"
          "[1] : The\Core\Application::onShutdown()"
          "[2] /home//core/rev20140516113043/Core/Application.php:960 The\Core\TemplateEngine\Template::__toString()"
          "[3] /home//core/rev20140516113043/Core/TemplateEngine/Template.php:23 The\Core\TemplateEngine\TemplateAdapter::run([Long string(10622)] '<!DOCTYPE html>"
          "<html lang="ru-RU">"
          "<head>"
          "    <meta charset="utf-8">"
          "    <title><?=$this->makeEcho...,array([Long string(214)] array(),object The\Core\TemplateEngine\TemplateRawInput,array([Long string(129)] array([Long string(...))"
          "[4] /home//core/rev20140516113043/Core/TemplateEngine/TemplateAdapter.php:30 eval()"
          "----------------------------------------------------------..."
          root@server:/#
          
          • +1
            Ну вы опять же в одной строке нашли открывающую и закрывающую скобку. Попробуйте например найти от 'Backtrace:' до '<title>'.
            • +1
              Ааа, при таком раскладе. Ну тогда да, мой вариант не катит:)
    • 0
      есть утилитка pcregrep с ключем -oNUMBER, которая позволяет возвращать только нужную группу
  • 0
    А тут можно изучить некоторые возможности grep играя с web-формой (и не только grep).
  • 0
    Некрасивая конструкция? Потрюкачим немного:
    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

    А как это работает этот пример? Я что-то не понял.

    P.S.: Спасибо за статью. Реквестирую такую же по sed!
    • +7
      Да, пример изначально алогичный. И само использование символьного класса для одного символа кажется бессмысленным.
      По шагам как происходит обработка этого момента:
      grep "[t]tyv" помещается в список процессов;
      — происходит преобразование символьного класса [t] в t;
      — grep начинает работу;
      Т.е. grep ищет ttyv, в то время как в список процессов попало [t]tyv.
      Надеюсь смог объяснить.
      • +1
        Всё было так просто :-) Спасибо за разъяснение.
  • +1
    Часто пользуюсь -l когда надо что бы grep показал в каком файле нашёл ответ:
    > grep -l "www.google.com" *.conf
    httpd-test.conf
    


    Или в совокупности с find:
    > find . -name "*.conf" -exec grep -l "ServerName" {} \;
    ./httpd-test.conf
    ./conf/httpd.conf


    • +1
      Можно без find'a:
      grep -Rl "ServerName" --include="*.conf" .

      find ваш тоже можно существенно ускорить:
      find . -name "*.conf" -exec grep -l "ServerName" {} \+
      • 0
        То было просто как упрощённый пример. find может много ещё сверху фильтров сделать типа времени и прочего.

        А по поводу "\+" не знал, спасибо. Почти как xargs получается.
    • +1
      а еще удобна -L, когда нужно найти файлы, в которых не встречается искомая конструкция
  • –4
    Хотелось бы добавить, что в windows есть не менее полезная команда findstr
    Например,
    netstat -anb | findstr 554
    • +15
      И как все виндовые утилиты командной строки, она ошеломляет возможностями и удобством.
      • 0
        Но все же, поиск по файлу/рекурсивный по директории может, нумеровать строки может, строки до и после паттерна так же отображает. Таким образом, на мой взгляд, все наиболее распространенные варианты использования линукс-аналога предоставляет.
        • +1
          Если есть возможность — лучше все-таки залить портированный grep.
          • +1
            На своих машинах у меня всегда стоят юниксовые тулзы. Но бывает, надо что-то быстро сделать на чужой машине, тогда подобное знание очень пригождается.
    • –1
      Хотелось бы добавить что пора прекратить насиловать труп BAT-файлов в Windows есть powershell, а в нём команда «Select-String» и прочие.
      • +1
        Не всегда нужна(удобна) пушка, для некоторых воробьев и рогатки хватает. Findstr удобен для 'быстрых' задач/действий. Согласен что скрипты удобнее на powershell писать.
        • –2
          Какая пушка? Это отличный инструмент на каждый день. BAT/CMD надо забыть, как страшный сон.
          • +3
            powershell не годится в качестве замены шелла, потому что он enveloping. В том смысле, что нормально в нём работать можно только с софтом, который готов к powershell'у, и отдаёт структурированные объекты. В условиях неструктурированного stdin powershell, мягко говоря, не очень удобен.
            • –1
              Вот уж не знаю чем он вам не угодил в таких случаях. Я на нём много программировал, когда у меня Винда была, никаких трудностей не испытывал. С утилитами, которые гонят «объекты» по пайпу просто удобнее, с остальными — примерно так же, как на «баше».
  • +9
    Про опцию -o ни слова, хотя она очень и очень полезна, особенно вместе с регэкспами.

    Шайтанство с --line-buffered тоже не раскрыто. А ведь новичков ждёт большой сюрприз с такой строчкой:

    tail -f /var/log/apache/access.log|grep myfile|grep 200

    С большой вероятностью вывода не будет довольно долго. Правильно:

    tail -f /var/log/apache/access.log|grep --line-buffered myfile|grep 200

    Комбинация grep foo|awk '{print $2}' заменима на awk '/foo/{print $2}'
    • 0
      Ах, спасибо за замену grep foo|awk '{print $2}' на awk '/foo/{print $2}'! Взял на вооружение.
  • +3
    grep -v # /etc/resolv.conf

    Не сработает как вы описали — с «#» тут начнётся комментарий. Существует так же fgrep (или grep -F), который ищет подстроку, а не регулярку, он быстрее и позволяет не экранировать спецсимволы регулярных выражений.
    • –5
      Что это за неуч минуснул?
    • +1
      # /bin/csh
      root@nm3:/ # grep -v # /etc/resolv.conf
      nameserver 8.8.8.8

      root@nm3:/ # /bin/tcsh
      root@nm3:/ # grep -v # /etc/resolv.conf
      nameserver 8.8.8.8

      root@nm3:/ # /bin/sh
      # grep -v # /etc/resolv.conf
      Usage: grep [OPTION]… PATTERN [FILE]…
      Try `grep --help' for more information.
      # grep -v "#" /etc/resolv.conf
      nameserver 8.8.8.8

      пояснения нужны?
      • +1
        Нужны. В каком Линуксе по-умолчанию стоит csh и с каких пор, для примеров на другом шелле, специально не указывается, что шелл другой?
        • 0
          Да я почти с этого статью начал.
          Распишем детальнее:
          root@nm3:/ # grep --version | grep grep
          grep (GNU grep) 2.5.1-FreeBSD
          root@nm3:/ # uname -o
          FreeBSD
          root@nm3:/ # id
          uid=0(root) gid=0(wheel) groups=0(wheel),5(operator)
          root@nm3:/ # echo $SHELL
          /bin/csh
          
          • –1
            У вас в тегах «Линукс».
            • –3
              Я думаю, что большинство примеров будут работать и в Линуксе, ну а уж выяснять в каком шелле требуется закавычивание, а в каком нет, уж извините, не буду )
              • +1
                И не нужно. Обычная вещь — при использовании шелла отличного от sh, указывается какой шелл вы имеете ввиду.
                • –1
                  А что же по вашему делает Борновский шелл таким привилегированным? :)
                  пройдёмся по дефолт-шеллам:
                  BusyBox ash
                  GoboLinux zsh
                  OpenBSD ksh
                  FreeBSD tcsh
                  Mac OS X tcsh / bash
                  Minix ash
                  AIX вроде ksh
                  Согласен, что может стоит указывать shell в котором делаются тесты, но уж никак не по причине того что это не sh
                  • +1
                    Ничего. Потому что я говорю не про bsh, не про bash, а про sh (именно этот шелл указан в большинстве шелл-скриптов, которые мне попадаются в самых разных ОС). Кроме того, в тегах «Линукс», а не все эти операционные системы, которые вы указали.
                    • –3
                      В тегах ещё и *nix, и все вышеперечисленные Unix-подобные, не говоря уж о FreeBSD, которая самый что ни на есть прямой потомок ATT UNIX
                    • –2
                      А что касается именования sh, то посмотрите куда он линкуется, под ним может скрываться несколько совершенно разных shell'ов.
                      Как пример:
                      root@DD-WRT:~# echo $SHELL
                      /bin/sh
                      root@DD-WRT:~# $SHELL --version
                      BusyBox v1.22.1 (2014-06-23 09:50:00 CEST) built-in shell (ash)

                      • +1
                        Знаю я куда он линкуется, ну и что? Когда в файле пишут !#/bin/sh, то пишут именно на sh, а не на том, куда он там линкуется.
                        • –3
                          Совсем не понимаете?
                          Это значит что по пути /bin/sh может быть хардлинк или симлинк на какой угодно шелл.
                          Как вы и просили выше пример линукса с дефолтным ash
                          • +1
                            Нет, это вы не понимаете.

                            sh — это вполне определённый язык. Например, «#» там комментарий. Неважно на какой шелл у вас там линк, «#» должна работать единообразно — быть комментарием.

                            Если вы используете другой шелл, то укажите какой, только и всего, не заставляйте всех гадать.
                            • –2
                              Ладно попробуем по шагам:
                              — sh — это командный интерпретатор. Ему можно передавать команды на исполнение.
                              — Они могут передаваться как из интерфейса командной строки так и из скриптов.
                              — Сейчас говорим только об интерфейсе командной строки (попутно вопрос — Откуда там могут взяться комментарии?)
                              — По пути /bin/sh может оказаться совсем не тот интерпретатор, который Вы ожидаете увидеть, то что он зовётся sh, совсем не значит, что там живёт sh

                              бузибокс:
                              /bin/sh --version
                              BusyBox v1.22.1 (2014-06-23 09:50:00 CEST) built-in shell (ash)

                              дебиан
                              ls -l /bin/sh
                              lrwxrwxrwx 1 root root 4 Mar 1 2012 /bin/sh -> dash

                              центос
                              # /bin/sh --version
                              GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

                              Надеюсь вы понимаете что между ash, dash и bash таки есть хоть какая-то разница.
                              Я понятнее не смогу объяснить.
                              • +3
                                Зачем вы это всё рассказываете?

                                sh — это интерпретатор и вполне конкретный язык. Да, многие шеллы с ним совместимы. Когда вы используете другой язык (не sh), надо это указать. Вот и всё. Не нужно уже несколько комментариев играть в Капитана Очевидность.
                              • +2
                                — По пути /bin/sh может оказаться совсем не тот интерпретатор, который Вы ожидаете увидеть, то что он зовётся sh, совсем не значит, что там живёт sh
                                Давайте не будем рассказывать сказки. По адресу /bin/sh живёт именно sh. Это может быть bash, dash, ksh или FigZnaetChroSh, но это точно не csh и у него есть вполне определённые правила. И, в частности, в главе Token Recognition чётко и недвусмысленно написано, что всё, что следует за символом # считается комментарием.
                                • –2
                                  Сказок никаких нет. Яж и не спорю, что /bin/sh — все Борн-шелл совместимые (даже в той же FreeBSD). И при исполнении сценариев в основном они стараются поддерживать Борн-шелл совместимость, хотя давно уже не удаётся, иначе не писалось бы кучу книг о переносимом Борн-шелл программировании. Вот только Вы опять упустили суть с чего начался вопрос. Мы говорим не о сценариях. В сценариях и С-шеллы интерпретируют # однозначно как комментарий и никак иначе. И если поместить строку с которой начался вопрос в скрипт и выполнить её /bin/csh'ем, то она гарантированно не отработает.

                                  Но, объясните откуда взялись комментарии в интерфейсе командной строки. Или в борн-совместимых шеллах можно в командной строке писать команды с комментариями?
                                  • +3
                                    иначе не писалось бы кучу книг о переносимом Борн-шелл программировании
                                    Непортабельно можно писать на любом языке, будь то C или Java.

                                    Но, объясните откуда взялись комментарии в интерфейсе командной строки. Или в борн-совместимых шеллах можно в командной строке писать команды с комментариями?
                                    Разумеется. Вот этого бреда:
                                    В сценариях и С-шеллы интерпретируют # однозначно как комментарий и никак иначе. И если поместить строку с которой начался вопрос в скрипт и выполнить её /bin/csh'ем, то она гарантированно не отработает.
                                    в них нету.

                                    Вернее это не совсем так: в bash'е подобное поведение можно включить (100500 опций — это «наше всё», да, разработчики GNU'сных утилит это любят), но понятно, что по умолчанию оно выключено.

                                    Зачем вообще может быть нужен, с позволения сказать, shell, который ведёт себя по разному в интерфейсе командной строки и при интерпретации скрипта? Я всегда думал, что только Microsoft мог создать подобный ужас. Сами себе вы можете создавать какие угодно проблемы, но по умолчанию интерактивный shell и неинтерактивный ведут себя одинаково. И, в частности, одинаково реагируют на #
                                    • +1
                                      Это стандартное поведение для С-шеллов.
                                      Более того зачастую в системе используются по умолчанию разные login- и script- шеллы.
                                      Ну в той же FreeBSD дефолтным логин-шеллом является tcsh, а скриптовым ash (т.е. они даже не одного пошиба, первый С-шный, второй Борновский)
                                      Если не ошибаюсь, то и в Mac OS X старых версий были tcsh и bash
                                      • 0
                                        Ну в той же FreeBSD дефолтным логин-шеллом является tcsh, а скриптовым ash
                                        Такого понятия, как «дефолтный скриптовый шелл», не существует. Какой шелл запрошен в первой строчке — такой и будет этот скрипт обрабатывать.
                                        • –1
                                          А если ни какой не запрошен? :)
                                          • 0
                                            А если ни какой не запрошен, то скрипт или будет работать — или нет, в зависимости от того, из какого шелла его запустили. А из других программ так вообще его не запустить. Так что нельзя #! пропускать никогда.
                                    • +1
                                      Аналогичная вариативность поведения наблюдается и у ksh с zsh (которые являются bourne-совместимыми с элементами C-шеллов). Например, zsh использует : для комментариев (и в командной строке, и в скриптах), но ещё и адекватно воспринимает # в скриптах (для shebang, как минимум).

                                      Возможно, что поведение csh/tcsh аналогично, я не тестировал.
                                      • –1
                                        Да я вот и не пойму на кой чёрт мне комменты в консоли? На утро пересматривать history с комментариями, чтобы понимать что спьяну вечером наворотил?

                                        Ну кто мешал при разборе optarg закавычить #
                                        Хотя, возможно, всё дело привычки. Это фряшники привыкли к разным скрипт/логин шеллам. Для линуксоидов это, действительно, кажется необычным.

                                        Но таки всем участникам обсуждения спасибо!
                                        В статье закавычил все #.
                                        • +1
                                          Да я вот и не пойму на кой чёрт мне комменты в консоли? На утро пересматривать history с комментариями, чтобы понимать что спьяну вечером наворотил?
                                          Например, чтобы не потерять набранную команду, когда нужно проверить какие-то предусловия. А следом взять её из history, поправить, если нужно, и выполнить. У меня это основной кейс.

                                          А набрать "<C-a>: <Ret>" мне подчас удобнее, чем, например, открыть второй терминал и привести его к тому же стейту.

                                          Хотя, возможно, всё дело привычки. Это фряшники привыкли к разным скрипт/логин шеллам. Для линуксоидов это, действительно, кажется необычным.
                                          Для тех, кто пользуется, например, zsh — ничего необычного (у меня скрипты — sh/bash, а интерактивная консоль — zsh). Неудобств не испытываю.
                                • 0
                                  /bin/sh это в 99.99999% случаев совместимый с ним шелл. Однако Большая часть скриптов коорые указывают sh в shebang используют bash и ожидают по /bin/sh обнаружить именно его. И ни о какой совместимости с sh там не думают. Ссылаться на /bin/sh удобно потому, что он там всегда есть, а bash может и не быть.

                                  Вернемся к началу: то, что вы не знаете о том, что есть "#" в sh-совместимых шелах это исключительно ваша проблема. Ко мне не раз такие вот как вы обращались с криками, что мои zsh скрипты не работают в их баше, хотя у них даже раширение стояло zsh.
                                  • 0
                                    Авторы нормальных скриптов знают, что /bin/sh должен быть совместим с оригинальным sh, а не bash или zsh. В Debian/Ubuntu это dash, и башизмов в системных скриптах с /bin/sh нет.
                                    • +4
                                      Зачем вы мне это рассказываете?
  • +5
    К моему удивлению никто из претендентов с обоими вопросами не справился. Двое, в принципе, не знали о существовании grep.


    (Дергая веком) К-к-к-ак?! Вы точно указали, что вакансия unix-админа?

    Большое спасибо за статью. Было интересно и познавательно.
    • +2
      К сожалению, эта история имела продолжение. Поиски проводились ещё дважды. Суммарно ещё человек 10. Одному пришлось отказать, т.к. вряд-ли работодатель смог бы его мотивировать через полгода-год. Из остальных был выбран самый молодой в надежде, что удастся его научить… Но, видимо, учитель я плохой…
      • 0
        Не понимаю. Вы искали на удалёнку или в каком-то конкретном городе? Может денег давали мало? Смотрю на примере своем и своих знакомых. Знаю как минимум пять людей (не считая меня), которые подобные задачки решат сходу спросонья.

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


        Тут имеется ввиду, что человек через полгода-год захотел бы больше денег или повышение в должности?
        • +3
          Удалёнку, со сменным идиотским графиком и монотонной и скучной работой без фантазии, хоть и с приличной оплатой (для удалёнки).
          • +1
            Больше $1k в месяц?
  • +1
    Задача которая меня всегда интересовала — как сделать рекурсивный поиск по директориям включая архивы? После винды, где тотал ищет очень быстро, этого порой очень нехватает.
    • +2
      deepgrep
    • +4
      ack, ag (the silver searcher). Удивлён, что о них ещё не написали. Ищут очень быстро, особенно ag. По бинарным файлам (архивам) grep ищет по умолчанию, ack и ag нужно специально это указывать, поскольку они создавались для поиска по исходному коду.
      Искать рекурсивно и в архивах можно примерно так:
      Скрытый текст
      Структура директории и содержание файлов:
      $ tree
      .
      ├── test.txt
      ├── test.zip
      └── ttt
          └── test.zip
      
      1 directory, 3 files
      $ cat test.txt 
      one two three four
      five
      
      six
      
      
      seven
      

      grep (пример из документации ack):
      $ grep seven $(find . -type f | grep -v '\.svn')
      ./test.txt:seven
      Binary file ./ttt/test.zip matches
      Binary file ./test.zip matches
      

      ack (простого решения не нашёл, его сейчас сложно заставить искать по бинарным файлам):
      $ ack --type-set archive:ext:zip --archive seven
      ttt/test.zip
      8:seven
      
      test.zip
      8:seven
      
      $ ack seven                                     
      test.txt
      7:seven
      
      $ # or like in grep:
      ack seven $(find . -type f | grep -v '\.svn')
      ./test.txt
      7:seven
      
      ttt/test.zip
      8:seven
      
      ./test.zip
      8:seven
      
      

      ag:
      $  ag --search-binary seven
      Binary file ttt/test.zip matches.
      
      test.txt
      7:seven
      
      Binary file test.zip matches.
      


      Да, есть ещё git grep, который очень быстро ищет по репозиторию git.
      • 0
        Имелся в виду не поиск по бинарным файлам архивов, а распаковка на лету. Если в незапакованном файле строка была, в запакованном её может не быть и наоборот.
      • 0
        Большое спасибо за ack, ag. До этого пользовался самописным скриптом для этой задачи на основе grep.
        Сейчас потестил, скорость поиска заметна даже на небольших проектах.
        Вывод также стал более читаем.
    • –1
      Не по архивам, но никто не написал: grep -R (постоянно использую для поиска в исходниках).
      • 0
        Выше несколько раз написали про -r (это синоним -R).
  • 0
    cat любите, а перенаправление вывода ненавидите? Почему?
    wc -l? А опция -c в grep почему не?
    • +1
      wc -l? А опция -c в grep почему не?
      В статье то же самое написано.
  • +2
    > Отбираем только строки с ip:
    > root@nm3:/ # grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" /etc/resolv.conf

    А строки с 999.999.999.999 тоже отберет?
    • +2
      Да, конечно, я сознательно не усложнял эту регулярку.
      grep -E '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b' /etc/resolv.conf
      была бы совсем нечитабельна.
      • +2
        Для анализа логов в большинстве случаев хватает и [0-9\.]{7,15}
  • 0
    Как открыть результат в nano? По типу grep foo /bar | cat
    • +2
      поставить nano2.2 и выше, где была добавлена эта фича.
      • –1
        У меня стоит 2.2.6, и когда я пробовал сделать grep foo /bar | nano, он создает отдельный файл вместо того, чтобы просто показать output в нано
    • 0
      Вот так
      nano <(grep foo file)

      можно ещё тут почитать про это mywiki.wooledge.org/ProcessSubstitution
  • +1
    Интересный пост о происхождении названия и истории grep: The history of grep, the 40 years old Unix command (English).
  • 0
    Странно, что grep кому-то приходится объяснять, без него в командной строке очень сложно и бессмысленно. Это одна из первых команд, которую вообще должен выучить линуксоид (ну, после head/tail).

    Вкупе с sed становится бронебойным инструментом обработки файлов (удаления кусков, форматирования и т.п.). Под виндой, увы, приходится писать мелкие скрипты на перле.
    • –3
      Под Виндой PowerShell есть.
  • 0
    Ох как тут не хватает поддержки PCRE…


    Попробуйте pcregrep. Не тратится время на придумывание BREs.
  • +1
    Автору спасибо за статью — весьма интересно. Особенно про «топ»-админов.

    По поводу ключей. М.б. лучше показывать их полные наименования? Не -А, а --after-context и т.д. Думается, что основная причина неиспользования «миллиардов» ключей в утилитах — лень их запоминать. Я вот их все не помню, но примерно знаю, что какая-то утилита что-то умеет и могу найти это. Полные ключики могут оставить в памяти человечей больший семантический отпечаток, нежели короткие.

    Оптимизация — корень всех зол. Конечно, когда у нас гига-бега-мега файл, есть смысл использовать ключики одной команды. Но вся суть *nix*'а в том, что это — набор утилит, каждая из которых делает только одну функцию.

    Прочитать? cat. Посчитать слова/строки? wc. И т.д.

    Я считаю, что будущим *nix*'ов должна стать большая пользователе-ориентированность:
    * Унификация интерфейсов/ключей/названий утилит
    * Способность оболочки оптимизировать, а не перекладывать это на пользователя. Т.е. если будущий дружественный humansh видит что-то типа cat bla | another --blabla, то пусть он сам переделывает это в более оптимальный вариант, убирая cat. Как это будет сделано — нам фиолетово.

    ps: Отсутствия комаров и мошек на шашлыках :)
    • 0
      Да, и я в большинстве случаев за разделение. Но иногда мы этим разделением доходим до абсурда и в ущерб производительности и удобству. К слову я как-то тестировал grep content -c file и grep content file | wc -l и grep выиграл почти на порядок. И на тот момент для меня это было принципиально.

      Ну а вообще принцип KISS — это тема для отдельной статьи :)
  • 0
    Не повезло вам с претендентами, возможно не там искали, я еще до того как устроился на первую работу помощником программиста уже научился пользоваться половиной команд grep из статьи, и догадывался о всех остальных возможностях, но тогда и с линуксом был знаком около года. Хотя наверное отдельная благодарность пользователю korjik, это он рассказал о чудо grep'е и приучил к редактору vim.
  • 0
    Интересно что там за админы такие в топе крупнейших фрилансерских сайтов, но даже веб-разработчику grep бывает довольно часто полезен. Сам не представляю как бы без него искал те или иные участки кода в запутанных системах имея под рукой только доступ к консоли. В этом плане grep экономит многие часы ручного перелопачивания кода.
    • –1
      Вы знаете… После этой статьи я смотрю на них снисходительнее. Выяснилось, что хабровчане с достаточно высоким рейтингом не знают элементарных основ (U|L)*n+x
  • +2
    Если вы считаете себя знаком Юникса, шелла или командной строки, то почитайте книгу «UNIX. Инструментальные средства» 1999 года.



    И поймете, как мало вы знаете :)
    • +2
      Если вы считаете себя знаком Юникса

      Я не считаю себя знаком и даже знатоком Юникса. А эту книгу я когда-то купил студентом за свои деньги. Я и сейчас не стесняюсь учиться, иначе будет скучно жить.
      • 0
        Книга у Вас осталась? Содержимое диска куда-нибудь выложить можете? А то я в продаже найти не могу.
        • 0
          Нет, книга была подарена лет 10 назад, а судьбы диска не помню.
        • 0
          В некоторых украинских интернет магазинов еще есть.
          • 0
            Спасибо. Просто купил оригинал у О'Рейлли. Причем третье издание.

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