czy możemy wydrukować ostatnie słowo każdej linii w systemie Linux za pomocą polecenia sed?


9

załóżmy, jeśli istnieje plik składający się z następujących wierszy, jeśli są

12345 567 7878 66

   er3 t45t y6y46y 


 4 lata 6 lat 656 lat 5 lat

   46y6 65y7 y66uyuy

 yy46y6y

Dane wyjściowe muszą wyglądać następująco:

66

y6y46y

y5y

y66uyuyy

y46y6y

Próbowałem sed 's/.* //g'nazwy pliku polecenia i kilku innych sedpoleceń, ale nie działa.

Czy mogę wiedzieć, jakie jest dokładne sedpolecenie?


Czy to konieczne sed?
coffeMug

Odpowiedzi:


8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Nadal wydrukowałby pustą linię dla każdej pustej linii. Aby tego uniknąć:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'

Pojedyncze alternatywa wyrażenie: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
jimmij

@jimmij - ten nie działa, jeśli ostatnia niepusta sekwencja jest również pierwszą i nie ma żadnych odstępów poprzedzających. Równie dobrze możesz zrobić to .*na ogonie, prawdopodobnie - wykluczasz wszystko oprócz spacji końcowych w / .*[^[:blank:]].
mikeserv



4

Jesteś prawie na miejscu. Wystarczy podać ostatnie słowo:

sed 's/^.* \([^ ][^ ]*\)/\1/g'

Co to robi:

  1. „^. *” usuwa wszystko na początku wiersza i spacji.
  2. „\ (...) \” pasuje do wzorca i zwraca go jako \ 1.
  3. „[^]” pasuje do wszystkiego bez spacji.

(Edytowane w celu dodania lepszego rozwiązania. Dzięki Hildred!)


1
Oto krótsze wyrażenie: sed -r 's/.* ([^ ]+)/\1/g'jeśli dozwolone są rozszerzone wyrażenia regularne, co zwykle ma miejsce.
mkalkov

Krótsza wersja, zastępująca to, czego nie chcesz zachować, zamiast tego, co chcesz zachować:sed 's/.* //'
Uriel

2

Możesz użyć odpowiedniego wzorca grepzamiast sed, na przykład:

grep -o "[a-Z0-9]*$"

W tym przykładzie [...]zawiera zakresy znaków uważane za odpowiednie dla „słowa” (w tym przypadku alfanumeryczne można dodać inne symbole, z których niektóre należy pominąć).


3
Zakłada się, że na końcu linii nie ma spacji. a-Zponieważ zakres nie ma większego sensu, nawet w ustawieniach regionalnych opartych na ASCII. Zauważ, że -ojest to rozszerzenie GNU.
Stéphane Chazelas

0

Jeśli kwalifikujesz słowo jako dowolną sekwencję 1 lub więcej niepustych znaków, wówczas odpowiedź brzmi zdecydowanie tak, i jest również bardzo prosta. To dlatego, że [[:blank:]]*i [^[:blank:]]*są boolean uzupełnienia oraz - pod warunkiem wszystkie znaki w łańcuchu są kompletne - [[:blank:]]*U [^[:blank:]]*można opisać ewentualne ciąg w taki sam sposób .*robi.

Jeśli w ciągu istnieje niekompletny znak lub w inny sposób nieprawidłowa sekwencja bajtów, nie można go z powodzeniem opisać od początku do końca - jak to czasami może się zdarzyć przy interpretacji ciągu w niewłaściwym kodowaniu. Aby zapewnić pełny znak na bajt w dowolnym ciągu, ustawienia narodowe C można wymusić:

LC_ALL=C sed ...

... które pozwoliłyby uniknąć problemów z opisywaniem struny od głowy do ogona za pomocą kompleksowego wzorca, takiego jak .*lub([ ]*[^ ]*)*

W pełni komplementarny wzór może powtarzać tyle razy, ile jest to konieczne, od lewej do prawej, długości dowolnego łańcucha, aby wylądować na ostatnim możliwym wystąpieniu bez przerwy w schemacie. Jest to definitywnie zwykły język.

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

ERE:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

Obie wersje nadal będą drukować puste linie, a to dlatego, że *gwiazda Kleene pasuje do zerowego lub więcej wystąpień wzoru. Najpierw dopasowuje zero lub więcej niepustych znaków, następnie zero lub więcej pustych znaków, a następnie zero lub więcej wystąpień zgrupowanych dopasowań, dopóki nie dopasuje ciągu w całości.

Po dopasowaniu tego wszystkiego magia dzieje się w zastępstwie - odniesienia zwracane przez grupy \1i \2są ostatnimi wystąpieniami każdego z nich. Tak więc po dokonaniu zamiany cały ciąg jest zastępowany tylko ostatnim wystąpieniem w wierszu zera lub więcej niepustych znaków - lub podgrupy \2.

Oczywiście działa to na każdy możliwy ciąg - nawet pusty - co oznacza, że ​​obie formy wypiszą znaki nowego wiersza dla wierszy zawierających tylko puste znaki lub wcale. Aby sobie z tym poradzić, możesz zrobić kilka rzeczy, ale najpierw sprawmy, aby klasa postaci była nieco łatwiejsza do pisania:

b='[:blank:]'

Teraz, aby drukować tylko wtedy, gdy wiersz zawiera jeden lub więcej niepustych znaków, możesz:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

ERE:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Przypadek BRE - podstawienie jest zawsze wykonywane i drukowane są tylko przestrzenie wzorcowe z przynajmniej jednym pozostałym znakiem.
  2. Przypadek ERE - próba podstawienia jest podejmowana tylko na przestrzeni wzorów zawierającej co najmniej jeden niepusty znak.

Każda forma będzie działać z dowolną metodą - o ile składnia jest poprawna.

-nWyłącza wyłącznik automatyczny druk przestrzeni wzorca, a pflagi na s///ubstitution lub /adres/ poleceń wypisuje jego wyniki tylko w przypadku powodzenia.

Tę samą logikę można zastosować, aby uzyskać dowolną {num} wystąpienie, takie jak:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

ERE:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... gdzie numoba wyrażenia regularne można zastąpić liczbą, aby wydrukować tylko{num} określone wystąpienie sekwencji niepustych znaków. Stosuje się tutaj nieco inną formę, aby zapewnić, że liczba nie zostanie przekrzywiona dla wiodącej spacji w ciągu.

Zauważ, że -Eprzełącznik ERE na sedjest obsługiwany zarówno w wersji BSD, jak i GNU, chociaż nie jest to jeszcze standardowa składnia POSIX.


Ładne wyjaśnienia, fajny hack, ale zauważ, że nie będzie działać z tradycyjnymi sed implementacjami (takimi jak Solaris / usr / bin / sed) i będzie droższy niż bardziej proste podejście (wyczerpuje pamięć z liniami wejściowymi o długości ponad 25 znaków z na przykład sed_su3z zestawu narzędzi Heirloom). Tak więc, chociaż podoba mi się odpowiedź, nie poleciłbym tego podejścia.
Stéphane Chazelas

Wydaje się też, że nie działa we FreeBSD.
Stéphane Chazelas

@ StéphaneChazelas - tak, wydajność jest naprawdę okropna dla czegoś takiego, ale może być bardzo skuteczna w wykrywaniu zdarzeń numerowanych. A dla końca linii sprawa s/.* \([^[:blank:]]\{1,\}\).*/\1/jest znacznie lepsza, ale trudniejsza, gdy w grę wchodzi wiele linii. Jednak pewnego dnia odkryłem, że 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]mogę całkiem skutecznie to wzmocnić. W każdym razie, dopóki nie ma rażącego błędu w logice, cieszę się - po prostu myślałem, że coś przeoczyłem.
mikeserv

@ StéphaneChazelas - och, a co do starszych seds - to trochę dziwne - powinno brzmieć zgodnie ze standardami. xrat mówi ... Standardowi programiści traktowali wspólne zachowanie historyczne, które wspierało "\n*", ale nie "\n\{min,max\}", "\(...\)*", lub "\(...\)\{min,max\}", jako nie zamierzony wynik konkretnej implementacji, i wspierali zarówno duplikację, jak i wyrażenia przedziałowe po podwyrażeniach i referencjach wstecznych.
mikeserv

@ StéphaneChazelas - A standard mówi ... Jeśli podwyrażenie, do którego odwołuje się odwołanie wsteczne, pasuje do więcej niż jednego łańcucha z powodu gwiazdki ( '*' )lub wyrażenia przedziałowego (patrz pozycja (5)), to odwołanie wsteczne będzie pasować do ostatniego (skrajnie prawy ) tych ciągów. Jestem pewien, że przetestowałem to w / minisedchoć - z pewnością testowałem coś dziwnego z / minisedw innym dniu.
mikeserv

0
sed 's/^ star.star //'  filename  or sed 's/^[[:blank:]]star.star[[:blank:]]//' filename

Analiza:

  • s - zastępstwo

  • / - początek wyrażenia do poszukiwania

  • ^ - od początku linii

  • [[:blank:]]* - jeśli na początku linii znajdują się puste miejsca

  • .* - dowolna postać

  • [[:blank:]] - i pusty znak

  • / - początek wyrażenia do podstawienia

  • / - koniec składni polecenia

PS: Napisałem gwiazdę w Commannd.


Jak można by to zastosować do danych podanych w pytaniu?
Kusalananda

@ Scott s/.*[[:blank:]]//działałby, chyba że na końcu linii będą puste.
Kusalananda

-1

Tak. Następujące polecenie sed najpierw usuwa wszystkie końcowe białe znaki ( s/ *$//), a następnie wszystko, aż do ostatniego białego znaku ( s/.* //). Prawdopodobnie warto zastąpić dosłowne białe znaki [[:blank:]], aby uchwycić tabulatory i inne znaki podobne do spacji.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.