Czy podczas korzystania z awk / pattern / {print „text”} / patern / {print „”} istnieje wzór ELSE?

22

Powiedzmy, że mam plik tekstowy taki jak:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Chcę użyć awkdo przetwarzania tych linii w inny sposób, na przykład

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

i chcę również wydrukować wszystkie pozostałe wiersze takimi, jakie są (bez tworzenia duplikatów już przetworzonych wierszy), w zasadzie potrzebuję znaku /ELSE/ { print $0}na końcu mojej awklinii.

Czy jest coś takiego?

Ali
źródło

Odpowiedzi:

27

Uproszczone podejście z awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Instrukcje Breakout of Pattern {Action}:

  • /R1/ { print "=>" $0;next}: Oznacza to, że linie /R1/z działaniem drukowania =>zostaną wykonane. nextoznacza, że ​​reszta instrukcji awk zostanie zignorowana i przejrzy się następny wiersz.

  • /R2/{print "*" $0;next}: Oznacza to, że linie pasujące pattern /R2/do akcji drukowania *zostaną wykonane. Po awkrozpoczęciu przetwarzania pierwsza pattern {action}instrukcja zostanie zignorowana, ponieważ pattern /R1/nie będzie to prawdą w przypadku wierszy zawierających /R2/. Tak więc druga pattern {action}instrukcja zostanie wykonana w wierszu. nextoznaczałoby to, że nie chcemy więcej przetwarzania i awknależycie przejdziemy do następnej linii.

  • 1drukuje wszystkie linie. Gdy podany jest tylko warunek, nie {action}, awk domyślnie używa {print}. Tutaj warunek jest 1interpretowany jako prawdziwy, więc zawsze się udaje. Jeśli dojdziemy do tego punktu, dzieje się tak, ponieważ pierwsza i druga pattern {action}instrukcja zostały zignorowane lub pominięte (dla linii niezawierających /R1/i /R2/), więc domyślna operacja drukowania zostanie wykonana dla pozostałych linii.

jaypal singh
źródło
Wydaje się, że nieznacznie wyprzedza wszystkie spośród opublikowanych rozwiązań.
Chris Down,
1
Nie jestem pewien, czy cukier składniowy jest tutaj właściwym terminem ... To tylko składnia.
Daniel Hershcovich
7

awkwdraża zwykłych podejrzanych, jeśli chodzi o warunki warunkowe. Warto stosować printfzamiast printpracy, którą chcesz wykonać podczas meczu.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'
Chris Down
źródło
Tak naprawdę nie potrzebujesz if-then-elsetego.
jaypal singh
1
Chociaż działa to doskonale, nie jest to idiomatyczne. Rozsądne użycie nextjest ważnym narzędziem w programowaniu awk.
dmckee,
2
Nie rozumiem sensu używania printftutaj. Jego jedyną zaletą (chyba że robisz bardziej zaawansowane formatowanie niż konkatenacja) jest to, że nie dodaje on nowego wiersza, co nie jest tu istotne.
Gilles 'SO - przestań być zły'
1
To sprzeczny z intuicją i zaskakujący wynik. Unadorned printmusi jedynie wyświetlać dane wyjściowe, $0podczas gdy printfmusi analizować ciąg formatu.
jw013,
5

Chris Down pokazał już, w jaki sposób można uzyskać wyrażenie regularne za pomocą wyrażenia „jeśli” w bloku. Możesz uzyskać ten sam efekt także na inne sposoby, chociaż jego rozwiązanie jest prawdopodobnie lepsze.

Jednym z nich jest napisanie trzeciego wyrażenia regularnego, które będzie pasowało tylko do tekstu niepasującego do innych, w twoim przypadku wyglądałoby to mniej więcej tak:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Uwaga: używa to zakotwiczonych wyrażeń regularnych - ^ na początku wyrażeń regularnych będzie pasować tylko na początku wiersza - oryginalne wzorce tego nie zrobiły, co spowalnia nieco dopasowanie, ponieważ sprawdza wszystkie znaki w wierszu, a nie przeskakując do następnej linii. Trzeci przypadek („else”) będzie pasował do linii, która zaczyna się od znaku, który nie jest „R” ([^ R]) lub zaczyna się od „R”, po którym następuje znak, który nie jest „1” lub „ 2 '(R [^ 12]). Dwa różne znaczenia ^ są nieco mylące, ale ten błąd został popełniony dawno temu i nie zostanie zmieniony w najbliższym czasie.

Aby użyć uzupełniających wyrażeń regularnych, naprawdę muszą być zakotwiczone, ponieważ w przeciwnym razie [^ R] pasowałby np. 1 za nim. W przypadku bardzo prostych wyrażeń regularnych, takich jak Ty, takie podejście może być przydatne, ale gdy wyrażenia regularne stają się bardziej złożone, podejście to stanie się niemożliwe do zarządzania. Zamiast tego możesz użyć zmiennych stanu dla każdej linii, na przykład:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Ustawia się to na zero dla każdej nowej linii, a następnie na 1, jeśli pasuje do jednego z dwóch wyrażeń regularnych, i na koniec, jeśli wciąż jest zero, wykonuje wydruk 0 $.

Alex Dupuy
źródło
Należy zauważyć, że w przypadku dużych plików oba są mniej wydajne niż używanie warunkowe (jak pokazano tutaj ). rfilepowtórzono zaledwie 10000 linii zestawu danych pytającego.
Chris Down,
4
if (!handled)Fuj! Użyj, nextaby przestać rozważać inne działania.
dmckee
+1 dla if (!handled). Ogólne, elastyczne rozwiązania wielokrotnego użytku są dobre. Co jeśli kolejna osoba, która ma to pytanie, chce wykonać więcej przetwarzania po wydrukowaniu? Odpowiedzi z nextnie obsługują tego.
Scott