Können wir das letzte Wort jeder Zeile unter Linux mit dem Befehl sed drucken?


9

Angenommen, es gibt eine Datei, die aus folgenden Zeilen besteht, falls dies der Fall ist

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 yy46y6y

Die Ausgabe muss folgendermaßen aussehen:

66

y6y46y

y5y

y66uyuyy

y46y6y

Ich habe den Befehl sed 's/.* //g'Dateiname und mehrere andere sedBefehle ausprobiert , aber es funktioniert nicht.

Kann ich wissen, was der genaue sedBefehl ist?


Ist es ein Muss zu verwenden sed?
Kaffee Tasse

Antworten:


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

Das würde immer noch eine leere Zeile für jede leere Zeile drucken. Um dies zu vermeiden:

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

Alternative mit einem Ausdruck : sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
Jimmy

@jimmij - das funktioniert nicht, wenn die letzte nicht leere Sequenz auch die erste ist und keine Leerzeichen davor stehen. Sie können es wahrscheinlich auch nur .*am Schwanz tun - Sie schließen sowieso alles andere als nachfolgende Leerzeichen mit aus .*[^[:blank:]].
Mikesserv


4

Du kannst es versuchen :

  • sed 's/.* //'
  • awk '{print $NF}'

4

Du bist fast da. Geben Sie einfach das letzte Wort an:

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

Was es macht:

  1. '^. *' löscht alles am Zeilenanfang und alle Leerzeichen.
  2. '\ (...) \' stimmt mit einem Muster überein und gibt es als \ 1 zurück.
  3. '[^]' passt zu allem ohne Leerzeichen.

(Bearbeitet, um eine bessere Lösung hinzuzufügen. Danke Hildred!)


1
Hier ist ein kürzerer Ausdruck: sed -r 's/.* ([^ ]+)/\1/g'Wenn erweiterte reguläre Ausdrücke zulässig sind, ist dies normalerweise der Fall.
mkalkov

Kürzere Version, indem Sie ersetzen, was Sie nicht behalten möchten, sondern was Sie behalten möchten:sed 's/.* //'
Uriel

2

Sie könnten ein adäquates Muster verwenden, grepanstatt sedzum Beispiel:

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

In diesem Beispiel [...]enthält das Zeichenbereiche, die für ein "Wort" als geeignet angesehen werden (in diesem Fall können alphanumerische Zeichen andere Symbole hinzugefügt werden, von denen einige maskiert werden müssen).


2
Dies setzt voraus, dass am Ende der Zeile kein Leerzeichen steht. a-ZEin Bereich ist selbst in ASCII-basierten Gebietsschemas nicht sehr sinnvoll. Beachten Sie, dass dies -oeine GNU-Erweiterung ist.
Stéphane Chazelas

0

Wenn Sie ein Wort so qualifizieren , dass es eine Folge von 1 oder mehr nicht leeren Zeichen bedeutet , lautet die Antwort definitiv Ja, und dies ist auch sehr einfach. Dies liegt daran , [[:blank:]]*und [^[:blank:]]*sind boolean ergänzt und - alle Zeichen in einer Zeichenfolge versehen sind komplett - [[:blank:]]*U [^[:blank:]]*in der gleichen Weise , jede mögliche Zeichenfolge beschreiben kann der .*Fall ist.

Wenn ein unvollständiges Zeichen oder eine anderweitig ungültige Bytesequenz in einer Zeichenfolge vorhanden ist, kann diese nicht von Kopf bis Ende erfolgreich beschrieben werden - wie dies manchmal bei der Interpretation einer Zeichenfolge in der falschen Codierung der Fall sein kann. Um ein vollständiges Zeichen pro Byte in einer beliebigen Zeichenfolge sicherzustellen, kann das Gebietsschema C wie folgt erzwungen werden:

LC_ALL=C sed ...

... wodurch Probleme vermieden werden, die die Zeichenfolge von Kopf bis Schwanz mit einem All-Inclusive-Muster wie .*oder beschreiben([ ]*[^ ]*)*

Ein vollständig komplementäres Muster kann die Länge eines Strings so oft wie nötig von links nach rechts wiederholen, um beim letztmöglichen Auftreten zu landen, ohne das Muster zu unterbrechen. Dies ist definitiv eine reguläre Sprache.

BRE:

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

EHE:

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

In beiden Versionen werden weiterhin leere Zeilen gedruckt. Dies liegt daran, dass der Kleene- *Stern mit null oder mehr Vorkommen eines Musters übereinstimmt. Es werden zuerst null oder mehr nicht leere Zeichen, dann null oder mehr leere Zeichen und dann null oder mehr Vorkommen der gruppierten Übereinstimmungen abgeglichen, bis die Zeichenfolge vollständig übereinstimmt.

Nachdem all dies übereinstimmt, geschieht die Magie beim Ersetzen - die Referenzen, die von Gruppen zurückgegeben werden \1und \2die letzten Vorkommen von jedem sind. Wenn also die Ersetzung erfolgt, wird die gesamte Zeichenfolge nur durch das letzte Vorkommen in einer Zeile mit null oder mehr nicht leeren Zeichen ersetzt - oder durch die Untergruppe \2.

Dies funktioniert natürlich für jede mögliche Zeichenfolge - auch für eine leere -, was bedeutet, dass beide Formulare Zeilenumbrüche für Zeilen drucken, die nur leere Zeichen oder gar keine enthalten. Um dies zu handhaben, gibt es einige Dinge, die Sie tun können, aber lassen Sie uns zunächst die Eingabe der Zeichenklasse etwas vereinfachen:

b='[:blank:]'

Um nur zu drucken, wenn eine Zeile ein oder mehrere nicht leere Zeichen enthält, können Sie Folgendes tun:

BRE:

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

EHE:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. BRE-Fall - Die Ersetzung wird immer durchgeführt und nur Musterbereiche mit mindestens einem verbleibenden Zeichen werden gedruckt.
  2. ERE-Fall - Die Ersetzung wird immer nur in einem Musterbereich versucht, der mindestens ein nicht leeres Zeichen enthält.

Beide Formulare funktionieren mit beiden Methoden - solange die Syntax korrekt ist.

Der -nSchalter deaktiviert das automatische Drucken des Musterbereichs, und das pFlag für die s///Ubstitution oder die /Adressbefehle /druckt die Ergebnisse nur, wenn dies erfolgreich ist.

Dieselbe Logik kann angewendet werden, um auch jedes {num}Vorkommen zu erhalten , wie:

BRE:

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

EHE:

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

... wobei die numin beiden regulären Ausdrücken durch eine Zahl ersetzt werden können, um nur das {num}angegebene Vorkommen einer Folge von nicht leeren Zeichen zu drucken . Hier wird eine etwas andere Form verwendet, um sicherzustellen, dass die Anzahl nicht für das führende Leerzeichen in einer Zeichenfolge verzerrt ist.

Beachten Sie, dass der -EERE-Wechsel zu sedsowohl in der BSD- als auch in der GNU-Version unterstützt wird, obwohl dies noch keine POSIX-Standardsyntax ist.


Schöne Erklärungen, netter Hack, aber beachten Sie, dass es mit herkömmlichen sed Implementierungen (wie Solaris / usr / bin / sed) nicht funktioniert und teurer sein wird als der einfachere Ansatz (erschöpft den Speicher mit Eingabezeilen, die länger als 25 Zeichen sind) die sed_su3aus der Heirloom-Werkzeugkiste zum Beispiel). Obwohl mir die Antwort gefällt, würde ich diesen Ansatz nicht empfehlen.
Stéphane Chazelas

Scheint auch in FreeBSD nicht zu funktionieren.
Stéphane Chazelas

@ StéphaneChazelas - Ja, die Leistung ist für so etwas wirklich schrecklich, aber es kann sehr effektiv sein, um nummerierte Vorkommen auszuwählen. Und für einen Zeilenende ist der Fall s/.* \([^[:blank:]]\{1,\}\).*/\1/weitaus besser, aber es ist schwieriger, wenn mehrere Leitungen beteiligt sind. Erst neulich habe ich jedoch festgestellt, dass 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]dies ziemlich effektiv gestützt werden kann. Wie auch immer, solange es keinen offensichtlichen Fehler in der Logik gibt, bin ich glücklich - ich dachte nur, ich hätte etwas verpasst.
Mikeserv

@ StéphaneChazelas - oh, und über die älteren seds - das ist ein bisschen komisch - es sollte nach Standard klingen. xrat sagt ... Die Standardentwickler betrachteten das allgemeine historische Verhalten, das unterstützt "\n*", aber nicht "\n\{min,max\}", "\(...\)*"oder "\(...\)\{min,max\}"als nicht beabsichtigtes Ergebnis einer bestimmten Implementierung, und sie unterstützten sowohl Duplizierungs- als auch Intervallausdrücke nach Unterausdrücken und Rückverweisen.
Mikeserv

@ StéphaneChazelas - Und der Standard sagt ... Wenn der durch die Rückreferenz referenzierte Unterausdruck aufgrund eines Sternchens ( '*' )oder eines Intervallausdrucks mit mehr als einer Zeichenfolge übereinstimmt (siehe Punkt (5)), muss die Rückreferenz mit der letzten (ganz rechts) übereinstimmen ) dieser Saiten. Ich bin mir ziemlich sicher, dass ich das getestet habe minised- obwohl ich neulich minisedsowieso etwas Seltsames getestet habe .
Mikeserv

-1

Ja. Der folgende Befehl sed entfernt zuerst alle nachfolgenden Leerzeichen ( s/ *$//) und dann alles bis einschließlich des letzten Leerzeichens ( s/.* //). Es lohnt sich wahrscheinlich, wörtliche Leerzeichen durch zu ersetzen [[:blank:]], um Tabulatoren und andere raumähnliche Zeichen zu erfassen.

$ 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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.