Niech sed ignoruje niepasujące wiersze

94

Jak mogę ustawić sedfiltrowanie pasujących wierszy według jakiegoś wyrażenia, ale ignorować niedopasowane wiersze, zamiast pozwolić im drukować?

Jako prawdziwy przykład chcę uruchomić scalac(kompilator Scala) na zestawie plików i odczytać z jego -verbosedanych wyjściowych .classutworzone pliki. scalac -verbosewyświetla kilka komunikatów, ale interesują nas tylko te z formularza [wrote some-class-name.class]. To, co obecnie robię, to ( |&jest to sposób basha 4.0 na przesłanie stderr do następnego programu):

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

Spowoduje to wyodrębnienie nazw plików z wiadomości, które nas interesują, ale także przepuści wszystkie inne wiadomości bez zmian! Oczywiście moglibyśmy zamiast tego zrobić:

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

co działa, ale wygląda bardzo podobnie do obejścia prawdziwego problemu, czyli instrukcji sedignorowania niepasujących wierszy z danych wejściowych. Jak więc to robimy?

Paggi
źródło
2
Zaakceptowaną odpowiedzią powinna być ta przesłana przez mouviciel: stackoverflow.com/a/1665574/869951
Oliver

Odpowiedzi:

90

Inny sposób z prostym sedem:

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

s///jest podstawieniem, tbez etykiety warunkowo pomija wszystkie kolejne polecenia, dusuwa linię.

Nie ma potrzeby używania perl ani grep.

(zredagowane za sugestią Nicholasa Rileya)

liori
źródło
3
W systemie OS X 10.8.2 musiałem oddzielić txi dużyć nowej linii zamiast średnika, jak otrzymywałem undefined label 'x;d;:x'.
davidchambers
6
Jeszcze lepiej: sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'(sugerowane w komentarzu do podobnego pytania ).
davidchambers
1
„t” będzie przenieść na końcu skryptu jeśli nie etykieta jest dostarczany, więc prościej: sed -e 's/.../.../' -e 't' -e 'd'.
Nicholas Riley
Ludzie nie znają znaczenia -eopcji, więc nie wspominaj o niej ogólnie.
Fredrick Gauss
246

Jeśli nie chcesz drukować niezgodnych wierszy, możesz użyć kombinacji

  • -n opcja, która mówi sedowi, żeby nie drukował
  • p flaga, która każe sedowi wypisać to, co zostało dopasowane

To daje:

sed -n 's/.../.../p'
mouviciel
źródło
3
Wadą tego podejścia jest to, że jeśli masz wiele pasujących wyrażeń, wynik zostanie również wydrukowany wiele razy. Na przykład: echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'wyświetli oba bariw oofoddzielnych wierszach. Chociaż odmiana goto-label również nie obsługuje wielu wzorców, ponieważ usunie linię, jeśli pierwszy wzorzec nie pasuje.
Rapsey,
@Rapsey to dlatego, że każesz mu wydrukować dwa razy. W pojedynczym poleceniu seda, które nakazałeś mu wydrukować dwukrotnie, każda instancja jest drukowana in-situ (lub może buforowana). Musiałbyś albo użyć potoku zamiast -e, albo wstawić 'p' tylko na ostatnie -e.
mikrobiologiczny
@Rapsey: zobacz moją odpowiedź na ten temat
Amessihel
@microbial, nie zadziała, ponieważ ostatnia flaga p będzie działać na każdym dopasowanym wierszu przez ostatnie wyrażenie podstawienia.
Amessihel
1

Użyj Perla:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Greg Hewgill
źródło
0

Rapsey poruszył istotną kwestię dotyczącą wyrażeń wielokrotnych podstawień.

  • Po pierwsze, cytując odpowiedź Unix SE , możesz „poprzedzić większość poleceń seda adresem, aby ograniczyć linie, do których się one odnoszą”.
  • Po drugie, możesz pogrupować polecenia w nawiasach klamrowych {}(oddzielone średnikiem ;lub nową linią)
  • Po trzecie, dodaj flagę print p do ostatniego podstawienia

Składnia:

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

Przykład (zobacz dokument tutaj, aby uzyskać więcej informacji):

  • Kod:

    sed -n -e '/^ha/ {s/h/k/gp;s/a/e/g}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Wynik:

    kaka
    
Amessihel
źródło
-1
sed -n '/.../!p'

Nie ma potrzeby zmiany.

Oczywiście
źródło