2012-06-09 9 views
5

muszę usunąć wiersze z pliku, które są po wzorzec1 i między wzór 2 i pattern3, jak poniżej:usuwać wierszy Po wzorzec1 i między pattern2 i pattern3 pomocą awk/sed/Perl

aaaaaaaa 
bbbbbbbb 
pattern1 <-----After this line 
cdededed 
ddededed 
pattern2 
fefefefe <-----Delete this line 
efefefef <-----Delete this line 
pattern3 
adsffdsd 
huaserew 

Proszę, zasugeruj, jak można to zrobić za pomocą awk, sed lub perla.

+1

Jeśli wzorzec1 ** ** ** przychodzi po pattern2 ** powinno nastąpić usunięcie? –

Odpowiedz

4
sed '/pattern1/,${ /pattern2/,/pattern3/{/pattern2/b; /pattern3/b; d;} };' file 

sformatowany:

/pattern1/,$ { 
    /pattern2/,/pattern3/ { 
     /pattern2/b; 
     /pattern3/b; 
     d; 
    } 
} 

Poradnik:

  • /pattern1/,$ jest zakres linii po pattern1 do końca pliku
  • /pattern2/,/pattern3/ jest zakres od linii między pattern2 i pattern3
  • /pattern2/b; i /pattern3/b; pomija pattern2 i pattern3 linie, które są w inny sposób uwzględnione w przedziale (patrz the sed faq)
  • d usuwa inne linie w zakresie

Aktualizacja

z komentarzy, wewnętrzny blok może zostać przepisany:

//!d 

gdzie:

  • // (pusty wzorzec) ostatni użyty regex (który w tym przypadku jest zarówno pattern2 i pattern3
  • ! odwraca następnego polecenia tak, że ma ona zastosowanie do wszystkich wyjątkiem liniach pasujące do wzorca
  • d usuwa te linie

Więc pełna, przepisany wzór:

/pattern1/,$ { 
    /pattern2/,/pattern3/ { 
     //!d 
    } 
} 
+0

+1 ale szkoda, że ​​trzeba powtarzać wzory. –

+2

Zgadzam się; to ograniczenie sed. Naprawdę nie ma na to lepszego sposobu (w sed). awk i perl na pewno mają bardziej eleganckie rozwiązania. – beerbajay

+2

@glennjackman: Nie musisz powtarzać wzoru: 'sed '/ pattern1 /, $ {/ pattern2 /,/pattern3/{// b; d;}}; '' –

3

wykorzystanie awk jak machiny państwowej:

awk ' 
    BEGIN {print_line = 1} 
    /pattern1/ {consider = 1} 
    consider && /pattern2/ {print_line = 0; print} 
    consider && /pattern3/ {print_line = 1} 
    print_line {print} 
' filename 
1

Kończenie Rosetta Stone:

perl -ne '++$saw_pattern1 if /pattern1/; 
      $inside = ($saw_pattern1 && /pattern2/) .. /pattern3/; 
      print unless $inside && ($inside > 1 && $inside !~ /E0$/)' \ 
    input 

Kod wykorzystuje Perl .. range operator.

W kontekście skalarnym .. zwraca wartość boolowską. Operator jest bistabilny, podobnie jak flip-flop, i emuluje operatora zasięgu linii (przecinek) sed, awk i różnych edytorów.Każdy operator .. zachowuje swój własny stan logiczny, nawet podczas wywoływania podprogramu, który go zawiera. Jest fałszywe, o ile jego lewy operand jest fałszywy. Gdy lewy operand jest prawdziwy, operator zakresu pozostaje prawdziwy, dopóki prawy operand nie jest prawdziwy, a operator zakresu staje się znowu fałszywy. Nie staje się ona fałszywa do następnego oszacowania operatora zakresu ...

Prawy argument operacji nie jest oceniany, gdy operator jest w stanie fałszywym, a lewy operand nie jest oceniany, gdy operator jest w stanie rzeczywistym . Pierwszeństwo jest nieco niższe niż || i &&. Zwrócona wartość jest pustym łańcuchem dla wartości false lub numerem kolejnym (rozpoczynającym się od 1) dla wartości true. Numer kolejny jest resetowany dla każdego napotkanego zakresu. Końcowy numer sekwencji w zakresie ma dołączony ciąg E0, który nie wpływa na jego wartość numeryczną, ale daje coś do wyszukania, jeśli chcesz wykluczyć punkt końcowy. Możesz wyłączyć punkt początkowy przez oczekiwanie na kolejny numer, aby być większa niż 1.

+0

Zaktualizowałem to za pomocą [znacznie bardziej zwięzłej odpowiedzi perl] (http://stackoverflow.com/a/10961632/124486). –

1

To może pracować dla Ciebie:

sed '/pattern1/,$!b;/pattern2/,/pattern3/!b;//!d' file 
2

Jeśli szukasz szybkiego rozwiązania w linii poleceń używając perla, jest to idealne rozwiązanie dla operatora flip-flop. Obecnie istnieją dwa sposoby, które na to pytanie może być interpretowany w przypadkach krawędziowych - oba te będą funkcjonować tak samo długo jak pattern1 przychodzi przed pattern2:

  1. Jeśli wzorzec1 przychodzi po pattern2 ale przed pattern3 usuwać wszystko pomiędzy wzorzec1 i pattern3

  2. lub, jeśli pa ttern1 przychodzi po pattern2 ale przed pattern3 zrobić nic chyba zobaczysz kolejny wzorcowi1.

Zanim zaczniemy, zwróć uwagę na arguement perl -p

-n    assume "while (<>) { ... }" loop around program 
-p    assume loop like -n but print line also, like sed 

Teraz do pierwszego, dam ci ..

perl -pe'$x ||= /7/; $_= "" if /5/ .. /8/ and $x' <(seq 1 10) 
1 
2 
3 
4 
5 
6 
9 
10 

$x ||= /7/: Ustawia $x do wartość zwracana /7/, gdy $x jest . /7/ zwróci true po dopasowaniu. Oznacza to, że $x zostaje ustawione na true, w pierwszym dopasowaniu i natura ||= nigdy nie ustawia zmiennej, kiedy jest już prawdą.

Następnie ustawia $_ = '', jeśli zakres zawiera się między /5/ i /8/, a ustawiono już wartość true dla $x. Zapamiętaj sposób działania zwarcia: a && b oznacza, że ​​uruchamiasz b tylko wtedy, gdy a ocenia na true.W tym przypadku sam fakt oceny a ustawi stan operatora flip-flop - tego właśnie chcemy; jednak chcemy tylko, aby wystąpił $_ = '', jeśli był już widziany 7.

Teraz do drugiej interpretacji quesiton tylko zmienić kolejność ...

perl -pe'$x ||= /7/; $_= "" if $x and /5/ .. /8/' <(seq 1 10) 

To wydrukować pełny zakres. Perl nie zacznie szukać /5/ dopóki nie znajdzie /7/. W naszym zakresie sekwencyjnym to się nie stanie.

BTW, naprawdę umieścić niektóre z tych odpowiedzi do wstydu, wiele pomieszczeń nie są wymagane ...

perl -pe'$x||=/2/;$_=""if$x&&/5/../8/' # secksey 
+1

Nie interpretuję pytania w żaden sposób. Pytanie jasno wskazuje, że wzorzec1 pojawia się * przed * wzorzec2. –

+0

@DennisWilliamson ** oba ** z nich poradzą sobie w tym przypadku dobrze. –

+0

@DennisWilliamson Nadal nie widzę, że 'pattern1' pojawia się przed' pattern2' jako oczywistym w pytaniu, przykład ma to, ale może być przypadkiem. ** Potrzebuję usunąć linie z pliku, który jest po wzorzec1 i między wzorzec 2 i wzorzec3 ** dlaczego nie można "wzorzec1" między "wzorzec2" i "wzorzec3"? ustawianie zakresu usuwania z "wzorzec1" do "wzorzec3" –