2009-07-27 28 views
34

Rozważmy wejście:wyjątkiem pierwszej i ostatniej linii z sed/Start /,/END/

=sec1= 
some-line 
some-other-line 

foo 
bar=baz 

=sec2= 
c=baz 

Jeśli chcesz przetwarzać tylko = Sec1 = mogę na przykład Wykomentuj sekcję przez:

sed -e '/=sec1=/,/=[a-z]*=/s:^:#:' < input 

... no, prawie.

To będzie komentować linii tym „= seC1 =” a „= s2 =” linie, a rezultatem będzie coś takiego:

#=sec1= 
#some-line 
#some-other-line 
# 
#foo 
#bar=baz 
# 
#=sec2= 
c=baz 

Moje pytanie brzmi: Jaki jest najprostszy sposób aby wykluczyć linie początkową i końcową z zakresu/START /,/END/w numerze?

Wiem, że dla wielu przypadków udoskonalenie pazurów "s :::" może dać rozwiązanie w tym konkretnym przypadku, ale jestem tutaj po ogólnym rozwiązaniu.

W "Sed - An Introduction and Tutorial" Bruce Barnett pisze: "Pokażę ci później, jak ograniczyć polecenie do, ale nie wliczając linii zawierającej określony wzorzec.", Ale nie byłem w stanie znaleźć, gdzie on to pokazuje .

W "USEFUL ONE-LINE SCRIPTS FOR SED" opracowany przez Eric Pementa, udało mi się znaleźć tylko przykład Inclusive:

# print section of file between two regular expressions (inclusive) 
sed -n '/Iowa/,/Montana/p'    # case sensitive 

Odpowiedz

31

To powinno załatwić sprawę:

sed -e '/=sec1=/,/=sec2=/ { /=sec1=/b; /=sec2=/b; s/^/#/ }' < input 

Dopasowuje między seC1 i s2 włącznie i następnie pomija pierwszą i ostatnią linię poleceniem b. Pozostawia to pożądane linie między sek1 i sec2 (wyłączne), a polecenie s dodaje znak komentarza.

Niestety, trzeba powtórzyć wyrazy regularne w celu dopasowania ograniczników. O ile wiem, nie ma lepszego sposobu na zrobienie tego. Przynajmniej możesz zachować regexps w czystości, nawet jeśli są używane dwa razy.

ten jest dostosowany od SED FAQ: How do I address all the lines between RE1 and RE2, excluding the lines themselves?

+0

Tak, to dokładnie to, co ja szukałem. Dzięki. –

+0

Dla osób na MacOS z BSD sed musisz użyć rzeczywistych linii specjalnych zamiast średników. Aby uzyskać więcej informacji, sprawdź [tę odpowiedź] (http://stackoverflow.com/a/15470635/2909897) – mbigras

+1

nie wiesz o innych wersjach, ale z GNU sed, można to łatwo zrobić za pomocą ''/ = sec1 = /,/= sec2 =/{//! s/^/# /} ''... from [manual] (https://www.gnu.org/software/sed/manual/sed.html#Regexp-Addresses)' puste wyrażenie regularne' // 'powtórzenia ostatnie wyrażenie regularne dopasowanie ' – Sundeep

1

można też użyć awk

awk '/sec1/{f=1;print;next}f && !/sec2/{ $0="#"$0}/sec2/{f=0}1' file 
11

Jeśli nie jesteś zainteresowany w liniach poza zasięgiem, ale po prostu nie-inclusive wariant Iowa/Przykład z Montany na pytanie (co mnie tu sprowadziło), możesz napisać z klauzulą ​​"z wyjątkiem pierwszego i ostatniego pasującego wiersza" z drugim sed:

sed -n '/PATTERN1/,/PATTERN2/p' < input | sed '1d;$d'

Osobiście uważam, że to nieco jaśniejszy (choć wolniej na dużych plikach) niż równowartość

sed -n '1,/PATTERN1/d;/PATTERN2/q;p' < input

+0

nice. także, druga wersja nie wysadza php z powodu tego brzydkiego $. – orolo

+0

Mam przeczucie, nie są one równoważne, jeśli istnieje więcej niż jeden z tych zakresów w strumieniu/pliku. – Dan

5

Innym sposobem byłoby

sed '/begin/,/end/ { 
     /begin/n 
     /end/ !p 
    }' 

/begin/n -> przeskoczyć linii, która ma "rozpocznij" wzór
/end/ !p -> wydrukuj wszystkie linie, które nie mają "końcowego" wzoru

Zrobione z sed tutorialu Bruce'a Barnetta http://www.grymoire.com/Unix/Sed.html#toc-uh-35a

0

Użyłem:

sed '/begin/,/end/{/begin\|end/!p}' 

Ten wyszuka wszystkie linie między wzorami, a następnie wydrukować wszystko nie zawierający wzory