Skomentuj wszystkie wiersze od ostatnio komentowanego wiersza do wiersza „foo”

12

Rozważ plik tekstowy users.txt:

#alice
#bob
charlie
dotan
eric

Muszę skomentować wszystko, od (wyłącznego) ostatnio komentowanego wiersza, aż do (włącznie) dotan. Oto wynik:

#alice
#bob
#charlie
#dotan
eric

Czy jest na to fajny sedoneliner? Będę zadowolony z każdego narzędzia, nie tylko sed, naprawdę.

Obecnie otrzymuję numer linii ostatnio komentowanego, ponieważ:

$ cat -n users.txt | grep '#' | tail -n1
  2 #bob

Następnie dodaję jeden i komentuję sed:

$ sed -i'' '3,/dotan/ s/^/#/' users.txt

Wiem, że mógłbym być sprytny i połączyć to wszystko z niektórymi bcw brzydką jednowarstwową. Z pewnością musi być czystszy sposób?

dotancohen
źródło

Odpowiedzi:

5

Co powiesz na

perl -pe '$n=1 if s/^dotan/#$&/; s/^[^#]/#$&/ unless $n==1;' file

lub ten sam pomysł w awk:

awk '(/^dotan/){a=1; sub(/^/,"#",$1)} (a!=1 && $1!~/^#/){sub(/^/,"#",$1);}1; ' file
terdon
źródło
7

Jeśli istniejące linie komentowane tworzą pojedynczy ciągły blok, możesz zamiast tego dopasować od pierwszej linii komentowanej, komentując tylko te linie aż do wzorca końcowego, który nie został jeszcze skomentowany

sed '/^#/,/dotan/ s/^[^#]/#&/' file

Jeśli istniejące komentarze nie są ciągłe, to z powodu chciwości charakteru dopasowania zakresu sed, myślę, że musisz zrobić coś takiego

tac file | sed '/dotan/,/^#/ s/^[^#]/#&/' | tac

tzn. dopasuj w górę od wzorca końcowego do „pierwszego” komentarza - oczywiście nie jest to tak wygodne, jeśli potrzebujesz rozwiązania na miejscu.

steeldriver
źródło
4

Możesz obsłużyć oba przypadki (wiersze z komentarzem w jednym ciągłym bloku lub przeplatane między wierszami bez komentarza) za pomocą pojedynczego sedwywołania:

sed '1,/PATTERN/{/^#/{x;1d;b};//!{H;/PATTERN/!{1h;d};//{x;s/\n/&#/g}}}' infile

Przetwarza tylko linie w 1,/PATTERN/zakresie. To e xzmiany trzymać przestrzeń w. przestrzeń wzoru za każdym razem, gdy linia jest komentowana (więc w buforze wstrzymania nigdy nie ma więcej niż jedna linia komentowana) i dołącza każdą linię, która nie jest komentowana do Hstarej spacji (gdy jest w pierwszej linii, 1di odpowiednio 1hsą również potrzebne do usunięcia początkowej pusta linia w buforze wstrzymania).
Kiedy osiągnie linię pasującą do WZORCA, dołącza ją również do Hstarego bufora, e xzmienia bufory, a następnie zamienia każdy \nznak ewline w obszarze wzorca \newline i a #(to znaczy, wszystkie linie w obszarze wzorca zaczynają się od #, uwzględnienie pierwszego wiersza jako pierwszego wiersza w przestrzeni wstrzymania jest zawsze komentarzem).
Z próbką infile:

alice
#bob
bill
#charlie
ding
dong
dotan
jimmy
#garry

bieganie:

sed '1,/dotan/{                   # if line is in this range    -start c1
/^#/{                             # if line is commented        -start c2
x                                 # exchage hold space w. pattern space
1d                                # if 1st line, delete pattern space
b                                 # branch to end of script
}                                 #                             -end c2
//!{                              # if line is not commented    -start c3
H                                 # append to hold space
/dotan/!{                         # if line doesn't match dotan -start c4
1h                                # if 1st line, overwrite hold space
d                                 # delete pattern space
}                                 #                             -end c4
//{                               # if line matches dotan       -start c5
x                                 # exchage hold space w. pattern space
s/\n/&#/g                         # add # after each newline character
}                                 #                             -end c5
}                                 #                             -end c3
}' infile                         #                             -end c1

wyjścia:

alice
#bob
bill
#charlie
#ding
#dong
#dotan
jimmy
#garry

więc komentuje tylko wiersze od (i wyklucza) #charliedo (włącznie) dotani pozostawia pozostałe wiersze bez zmian.
Jasne, zakłada to, że przed dopasowaniem linii zawsze jest przynajmniej jedna linia z komentarzem PATTERN. Jeśli tak nie jest, możesz dodać dodatkową kontrolę przed wymianą:/^#/{s/\n/&#/g}

don_crissti
źródło
Dziękuję, będę musiał sporo nauczyć się z tej odpowiedzi!
dotancohen
Poczekaj, musiałem spieprzyć. Nie chodzi o ostatnią serię komentowanych linii? Nie, rozumiem. Ostatnia seria + Dotan. Cholernie sprytny.
mikeserv
1
Zawsze znajdziesz najlepsze pytania. Cholerny Dotan kazał mi na chwilę rzucić - może nadal tak jest, jeszcze go nie testowałem. dzięki, don.
mikeserv
2

Oto kolejna sed:

sed  -e:n -e'/\n#.*\ndotan/!{$!{N;/^#/bn'      \
-eb  -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g' \
-et  -e\} -eP\;D <in >out

Tak, jak prosisz. Po prostu działa na stosie - buduje go, gdy jest to konieczne i tak długo, jak to konieczne między wystąpieniami komentowanych linii, i zrzuca stary bufor na korzyść nowej komentowanej linii dalej na wejściu, gdy ją znajdzie. Obrazek...

wprowadź opis zdjęcia tutaj

Przepraszam, nie wiem, dlaczego to zrobiłem. Ale przyszło mi do głowy.

W każdym razie sedrozkłada swoje bufory między każdą ostatnią komentowaną linią w dowolnej serii, nigdy nie zatrzymując ani jednego więcej w swoim buforze, niż jest to konieczne do dokładnego śledzenia ostatniego komentowanego wystąpienia, a jeśli w dowolnym momencie napotka ostatnią linię podczas tej operacji, spróbuje końcowa ginstrukcja wykonania gałęzi i odgałęzienie tszacują, że cały bufor zostanie wydrukowany, w przeciwnym razie Pzrzuci wszystkie linie, które zwolni z bufora, gdy tylko to zrobi.

Wydaje mi się, że właśnie to przywodziło na myśl akordeon ...

printf %s\\n   \#alice \#bob charlie dotan eric \
               \#alice \#bob charlie dotan eric \
               \#alice \#bob charlie dotan eric |
sed  -e:n -e'l;/\n#.*\ndotan/!{$!{N;/^#/bn'     \
-eb  -e\} -e'/^#/s/\(\n\)\(dotan.*\)*/\1#\2/g'  \
-et  -e\} -eP\;D

#alice
#alice\n#bob$
#alice\n#bob\ncharlie$
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob\ncharlie\ndotan\neric\n#alice$
#bob\ncharlie\ndotan\neric\n#alice\n#bob$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie$
#bob\ncharlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
#bob
charlie\ndotan\neric\n#alice\n#bob\ncharlie\ndotan$
charlie
dotan\neric\n#alice\n#bob\ncharlie\ndotan$
dotan
eric\n#alice\n#bob\ncharlie\ndotan$
eric
#alice\n#bob\ncharlie\ndotan$
#alice
#bob\ncharlie\ndotan$
#bob\ncharlie\ndotan\neric$
#bob
#charlie
#dotan
eric

Jest tylko jedna różnica między tym poleceniem a powyższym i jest to lpolecenie ook na górze. Kiedy lprzyglądamy się działającej sedprzestrzeni wzorcowej, możemy lepiej zrozumieć, co dzieje się za kulisami i lepiej zrozumieć, w jaki sposób kierować jej wysiłkami.

W tym przypadku możemy obserwować seddane wejściowe stosu, dopóki nie znajdzie drugiego wystąpienia danych \n#.*\ndotanwejściowych, i kiedy zacznie drukować poprzednią linię na raz. To trochę fajne. Wiele się nauczyłem, pracując nad tym.

mikeserv
źródło
Bardzo ładnie, dziękuje! Ostatni akapit z objaśnieniami jest wspaniały, poświęcę również sporo czasu na naukę tego wpisu. Niezły stos!
dotancohen
1
@dotancohen - to było naprawdę dobre pytanie. Spójrz na edycję, aby zobaczyć stos .
mikeserv
2
W historii edycji zauważam wpis Handle many dotans. Jestem pewien, że to najgorszy koszmar mojej żony.
dotancohen
1
@dotancohen - tak, to było trudne. Rzeczy, jak #\ndotan\ndotantrudno jest dla tych rzeczy. Mam na myśli to, kiedy mówię to dobre pytanie. I pomyśleć Mam to po prostu idealne, ale jeden problem, który mógłby uruchomić pod jest, jeśli komentarz bloki są oddzielone 1000 linii - że będzie zwalniać. Możesz włożyć coś s/\n/&/150;tw stylu przed pierwszą /\n#rzeczą, aby wyłamać bufor, jeśli obejmuje on na przykład 150 linii. A tak w ogóle, może to tylko co ona czekali przez cały czas !
mikeserv