Отличается ли синтаксис grep от регулярных выражений?

Я хочу извлечь имя удаленных пакетов отсюда "cat /var/log/dpkg.log | grep 'remove'"

 2013-09-09 15:57:34 remove activity-log-manager:i386 0.9.4-0ubuntu6.2 <none>
 2013-09-09 15:57:35 remove activity-log-manager-control-center:i386 0.9.4-0ubuntu6.2 <none>
 2013-09-09 15:57:38 remove alacarte:all 3.6.1-0ubuntu3 <none>
 2013-09-09 15:57:41 remove deepin-software-center:all 2.1.2.1~precise~NoobsLab.com <none>

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

(?<=remove)(.*?)(?=:)

Но это не работает:

cat /var/log/dpkg.log | grep 'remove' | grep '(?<=remove)(.*?)(?=:)'

Чего мне здесь не хватает?

2
задан 22 September 2013 в 15:10

2 ответа

Со страницы руководства grep:

grep ищет именованные входные ФАЙЛЫ (или стандартный ввод, если не указано ни одного файла, или если в качестве файла указан один дефис-минус (-) name) для строк, содержащих совпадение с заданным PATTERN.

Насколько я знаю, grep не имеет возможности редактировать строки, которые ему соответствуют; Я бы использовал sed или, возможно, tr для этого. Любое из следующего должно получить то, что вы хотите:

cat /var/log/dpkg.log | grep 'remove' | sed 's/.*remove \([^:]*\):.*/\1/'
cat /var/log/dpkg.log | grep 'remove' | sed -E 's/.*remove ([^:]*):.*/\1/'
cat /var/log/dpkg.log | sed -n '/remove/s/.*remove \([^:]*:\).*/\1/p'
cat /var/log/dpkg.log | sed -nE '/remove/s/.*remove ([^:]*):.*/\1/p'

Я, честно говоря, не уверен, что ваши (?<=remove)(.*?)(?=:) пытаются достичь. В регулярных выражениях скобки используются для определения групп захвата: вы можете видеть, что я использовал их в командах sed здесь - там все сопоставленное будет заменено содержимым группы захвата /1, первой группы быть определенным.

0
ответ дан 22 September 2013 в 15:10

Существует общее ядро ​​синтаксиса регулярных выражений, но есть разные разновидности. Кажется, что ваше выражение содержит некоторые особенности, специфичные для Perl, в частности, использование сложных обходных утверждений , описывающих начало и конец сопоставляемого шаблона, тогда как grep по умолчанию использует базовое регулярное выражение (BRE) синтаксис, который поддерживает только более простой набор этих совпадений нулевой длины , таких как line- (^, $) и якоря слов (\>, \<).

Вы можете включить поддержку perl-совместимого регулярного выражения (PCRE) в grep, используя ключ командной строки -P (хотя обратите внимание, что страница руководства в настоящее время описывает ее как «экспериментальную»). В вашем случае вы, вероятно, хотите, чтобы ключ -o также печатал только соответствующий шаблон, а не всю строку, т. Е.

cat /var/log/dpkg.log | grep 'remove' | grep -oP '(?<=remove)(.*?)(?=:)'

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

echo "2013-09-07 08:31:44 remove cifs-utils 2:5.1-1ubuntu2 <none>" | grep -oP '(?<=remove)(.*?)(?=:)'
 cifs-utils 2

Возможно, вы захотите взглянуть на awk, например,

cat /var/log/dpkg.log | awk '$3 ~ /remove/ {sub(":.*", "", $4); print $4}'

Помимо BRE и PCRE, Gnu grep имеет дополнительный режим, называемый расширенным регулярным выражением (ERE), определяемым переключателем командной строки -E. Страница руководства отмечает, что

In  GNU grep,  there is  no difference in available functionality 
between basic and extended syntaxes.

Однако вы должны заметить, что «отсутствие различий в доступной функциональности» не означает, что не означает , что синтаксис тот же. Например, в BRE символ + обычно обрабатывается как литерал и становится только модификатором, означающим «один или несколько экземпляров предыдущего регулярного выражения», если его экранировать, т. Е.

$ echo "123.456" | grep '[0-9]+\.[0-9]+'
$ echo "123.456" | grep '[0-9]\+\.[0-9]\+'
123.456

, тогда как для ERE это совершенно противоположное

$ echo "123.456" | grep -E '[0-9]+\.[0-9]+'
123.456
$ echo "123.456" | grep -E '[0-9]\+\.[0-9]\+'

Аналогичное различие применяется для sed, вызванного без и с переключателем -r.

0
ответ дан 22 September 2013 в 15:10

Другие вопросы по тегам:

Похожие вопросы: