Попросите sed игнорировать несовпадающие строки

94

Как я могу сделать sedстроки соответствия фильтрам в соответствии с некоторым выражением, но игнорировать несовпадающие строки, вместо того, чтобы позволить им печатать?

В качестве реального примера я хочу запустить scalac(компилятор Scala) для набора файлов и прочитать из его -verboseвывода .classсозданные файлы. scalac -verboseвыводит кучу сообщений, но нас интересуют только сообщения формы [wrote some-class-name.class]. В настоящее время я делаю следующее ( |&это способ bash 4.0 передать stderr следующей программе):

$ scalac -verbose some-file.scala ... |& sed 's/^\[wrote \(.*\.class\)\]$/\1/'

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

$ scalac -verbose some-file.scala ... |& grep '^\[wrote .*\.class\]$' |
  sed 's/^\[wrote \(.*\.class\)\]$/\1/'

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

Paggas
источник
2
Принятый ответ должен быть от mouviciel: stackoverflow.com/a/1665574/869951
Оливер

Ответы:

90

Другой способ с простым sed:

sed -e 's/.../.../;t;d'

s///- замена, tбез метки условно пропускает все последующие команды, dудаляет строку.

Нет необходимости в perl или grep.

(отредактировано по предложению Николаса Райли)

Лиори
источник
3
В OS X 10.8.2 мне пришлось разделить txи dиспользовать новую строку, а не точку с запятой, как я получал undefined label 'x;d;:x'.
davidchambers
6
Еще лучше: sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'(предложено в комментарии к аналогичному вопросу ).
davidchambers
1
«т» перечислит до конца сценария , если метка не поставляется, так проще: sed -e 's/.../.../' -e 't' -e 'd'.
Николас Райли
Люди не знают значения -eопции, поэтому вообще не упоминайте о ней.
Фредрик Гаусс
246

Если вы не хотите печатать строки, которые не совпадают, вы можете использовать комбинацию

  • -n опция, которая сообщает sed не печатать
  • p флаг, который сообщает sed печатать то, что соответствует

Это дает:

sed -n 's/.../.../p'
Mouviciel
источник
3
Одним из недостатков этого подхода является то, что если у вас есть несколько совпадающих выражений, результат также будет напечатан несколько раз. Например: echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'будет выводить обе barи oofв отдельных строках. Хотя сорт goto-label также не может обрабатывать несколько шаблонов, так как он удалит строку, если первый шаблон не совпадает.
Rapsey
@Rapsey, потому что вы говорите ему напечатать дважды. В единственной команде sed, которую вы указали для печати дважды, каждый экземпляр печатается на месте (или, возможно, в буфере). Вам нужно будет либо использовать конвейер вместо -e, либо поставить «p» только в последнем -e.
микробный
@Rapsey: см. Мой ответ по этому поводу
Амессихель
@microbial, это не сработает, так как последний флаг p будет работать в каждой строке, совпадающей с последним выражением подстановки.
Амессихель,
1

Используйте Perl:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Грег Хьюгилл
источник
0

Рэпси поднял уместный вопрос о выражениях множественных замен.

  • Во-первых, цитируя ответ Unix SE , вы можете «префикс большинства команд sed с адресом, чтобы ограничить строки, к которым они применяются».
  • Во-вторых, вы можете группировать команды в фигурных скобках {}(разделенных точкой с запятой ;или новой строкой)
  • В-третьих, добавьте флаг печати p на последнюю замену

Синтаксис:

sed -n -e '/^given_regexp/ {s/regexp1/replacement1/flags1;[...];s/regexp1/replacement1/flagsnp}'

Пример (подробнее см. Здесь документ ):

  • Код:

    sed -n -e '/^ha/ {s/h/k/gp;s/a/e/g}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Результат:

    kaka
    
Амессихель
источник
-1
sed -n '/.../!p'

В замене нет необходимости.

Очевидно
источник