Wie drucke ich nur die letzte Spalte?

25
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine

Wie kann ich etwas "MAGIC" machen, um diese Ausgabe zu bekommen ?:

three
six
nine

UPDATE: Ich brauche es nicht auf diese Weise, ich brauche eine allgemeine Lösung, damit egal wie viele Spalten in einer Zeile sind, zB: awk zeigt immer die letzte Spalte an.

LanceBaynes
quelle
2
Lance, bitte recherchiere deine Fragen, bevor du fragst. Wenn Sie in Google nach der Betreffzeile Ihrer Beiträge suchen, wird die Antwort in den Snippents angezeigt. Die Suche nach "awk last column" gibt mehrere gute Antworten, beginnend mit Ergebnis 1. Außerdem lohnt es sich , diesen 5-minütigen awk-Primer vollständig durchzulesen , damit Sie wissen, was in Zukunft möglich ist.
Caleb

Antworten:

6

Es kann auch nur mit durchgeführt werden 'bash', ohne 'sed', 'awk'oder 'perl':

echo -e 'one two three\nfour five six\nseven eight nine' |
  while IFS=" " read -r -a line; do
    nb=${#line[@]}
    echo ${line[$((nb - 1))]}
  done
jfg956
quelle
Hmm, oder auch, wenn Ihre Eingabe tatsächlich durch Leerzeichen getrennt ist:... | while read -r line; do echo ${line##* }; done
Glenn Jackman
@glenn: Dies war meine erste Idee, aber als ich das 'read'-Handbuch las, sah ich diese Array-Funktion, die ich nützlich fand. Es kann auch leicht modifiziert werden, um jedes von rechts indizierte Feld zu erhalten.
jfg956
2
bashDer Array-Index wird arithmetisch ausgewertet. Dies echo ${line[nb - 1]}ist ausreichend. Wie zu sprechen bash, können Sie einfach die „nb“ Dinge überspringen echo ${line[-1]}. Eine tragbare Alternative der später: echo ${line[@]: -1]}. (Siehe Stephane Chazelas 'Kommentar zu negativen Indizes an anderer Stelle.)
manatwork
53

Versuchen:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'
Sean C.
quelle
Ich habe den Q
LanceBaynes 20.07.11
Bitte beachten Sie, dass awk auf 99 Felder beschränkt ist ...: / Dies hat mich in den letzten Tagen nur gebissen ( ps -ef | awk '{ print $NF }'einige Zeilen wurden abgeschnitten ...) Perl hat diese Einschränkung nicht. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Traditionelles Awk hat ein Limit von 99 Feldern in einem Datensatz. Da einige Awk-Implementierungen, wie Tru64, die Eingabe auch dann aufteilen, wenn Sie nicht referenzieren Um dieses Problem zu umgehen, setzen Sie "FS" auf ein ungewöhnliches Zeichen und verwenden Sie "split". "
Olivier Dulac
@OlivierDulac Welche awkImplementierungen haben diese Einschränkung? Ich habe es nie gesehen. Mein mawkWille erstickt 32768aber mein gawkund igawkkann mit Millionen glücklich umgehen. Sogar meine Busybox awkkann mit Millionen umgehen. Ich bin noch nie auf eine awkgestoßen, die nicht mit 100 Feldern umgehen kann, das ist schließlich eine winzige Zahl. Sind Sie sicher, dass die Informationen immer noch relevant sind? Auch unter Solaris?
Terdon
@terdon, sieh dir die Links in meinem Kommentar an ^^ (und glaub mir, einige "Legacy" -Systeme können in manchen Umgebungen eine loooooooooo lange Zeit überleben. Auf manchen hat der Teerextrakt nach "/" einige der nützlichen nicht builtins (noch $ BASH_SOURCE, zum Beispiel), awk choke auf NF> 99, etc ... :()
Olivier Dulac
@OlivierDulac fair genug. Ich bin einfach nicht darauf gestoßen. Ich hoffe, es ist heute verschwindend selten, da 99 eine winzige Zahl ist.
Terdon
14

Es ist einfacher als du denkst.

$ echo one two three | awk '{print $NF}'
three
Bahamat
quelle
11

Versuchen Sie grep(kürzer / einfacher, aber 3x langsamer als awkaufgrund der Verwendung von Regex):

grep -o '\S\+$' <(echo -e '... seven eight nine')

Oder ex(noch langsamer, aber es druckt den gesamten Puffer nach Abschluss, was nützlicher ist, wenn er an Ort und Stelle sortiert oder bearbeitet werden muss):

ex -s +'%s/^.*\s//g' -c'%p|q!' <(echo -e '... seven eight nine')
ex +'%norm $Bd0' -sc'%p|q!' infile

Ersetzen Sie -sc'%p|q!'durch , um die Position zu ändern -scwq.

Oder bash:

while read line; do arr=($line); echo ${arr[-1]}; done < someinput

Performance

Angesichts der generierten 1 GB-Datei über:

$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile

Ich habe die Parsing-Zeitstatistik durchgeführt (lief ~ 3x und nahm die niedrigste, getestet auf MBP OS X):

  • mit awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • mit grep:

    $ time grep -o '\S\+$' datafile > /dev/null
    real    0m36.731s
    user    0m36.244s
    sys 0m0.401s
    
    $ time grep -o '\S*$' datafile > /dev/null
    real    0m40.865s
    user    0m39.756s
    sys 0m0.415s
  • mit perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • mit rev+ cut:

    $ time (rev|cut -d' ' -f1|rev) < datafile > /dev/null
    $ time rev datafile | cut -d' ' -f1 | rev > /dev/null
    real    1m10.342s
    user    1m19.940s
    sys 0m1.263s
  • mit ex:

    $ time ex +'%norm $Bd0_' -sc'%p|q!' datafile > /dev/null
    real    3m47.332s
    user    3m42.037s
    sys 0m2.617s
    $ time ex +'%norm $Bd0' -sc'%p|q!' datafile > /dev/null
    real    4m1.527s
    user    3m44.219s
    sys 0m6.164s
    $ time ex +'%s/^.*\s//g' -sc'%p|q!' datafile > /dev/null
    real    4m16.717s
    user    4m5.334s
    sys 0m5.076s
  • mit bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s
Kenorb
quelle
6
... | perl -lane 'print $F[-1]'
Glenn Jackman
quelle
Wichtige Punkte -a:, Felder automatisch in ein @FArray aufteilen ; -lschneidet ab $/(Trennzeichen für Eingabesätze) und speichert es unter $\(Trennzeichen für Ausgabesätze). Da keine Oktalzahl angegeben ist -l, wird das Original $/gedruckt (Zeilenenden). -nSchleifencode; -eCode sofort danach ausführen. Sehen man perlrun.
Jonathan Komar
5

Dies kann auch erfolgen mit 'sed':

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$/\1/'

Aktualisieren:

oder einfacher:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'
jfg956
quelle
Einfacher ist besser!
mdpc
@mdpc: Ich stimme zu, aber da die kompliziertere Lösung bereits veröffentlicht wurde, habe ich beschlossen, sie beizubehalten.
jfg956
5

Oder mit cut:

echo -e 'one two three\nfour five six\nseven eight nine' | cut -f 3 -d' '

Dies entspricht jedoch nicht der Anforderung einer "allgemeinen Lösung". Mit revzweimal können wir auch dieses Problem lösen:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev
Tim
quelle
Ich denke nicht, dass 'rev' auf allen Unix-Systemen (AIX, Solaris, ...) zu finden ist oder auf allen Linux-Systemen installiert ist, aber eine nette alternative Lösung.
jfg956
1
+1 für doppelte Umdrehungen, aber als Randnotiz, revfunktioniert meines Wissens nicht mit 'breiten' Zeichen , sondern nur mit Einzelbyte-Zeichen.
Marcin
2

Mit können awkSie zunächst prüfen, ob mindestens eine Spalte vorhanden ist.

echo | awk '{if (NF >= 1) print $NF}'

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'
gezu
quelle
3
Oder weniger wörtlich awk 'NF{print $NF}'.
Manatwork
1

In Perl kann dies wie folgt erfolgen:

#!/usr/bin/perl

#create a line of arbitrary data
$line = "1 2 3 4 5";

# splt the line into an array (we call the array 'array', for lolz)
@array = split(' ', $line);

# print the last element in the array, followed by a newline character;
print "$array[-1]\n";

Ausgabe:

$ perl last.pl
5
$

Sie können auch eine Datei durchlaufen. Hier ist ein Beispielskript, das ich geschrieben habe, um eine Datei namens budget.dat zu analysieren

Beispieldaten in budget.dat:

Rent              500
Food              250
Car               300
Tax               100
Car Tax           120
Mag Subscription  15

(Sie können sehen, ich brauchte nur die "letzte" Spalte zu erfassen, nicht nur Spalte 2)

Das Drehbuch:

#!/usr/bin/perl
$budgetfile = "budget.dat";
open($bf, $budgetfile)
        or die "Could not open filename: $filename $!";


print "-" x 50, "\n";
while ( $row = <$bf> ) {
        chomp $row;
        @r = split (' ', $row);
        print "$row ";
        $subtotal += $r[-1];
        print "\t$subtotal\n";
}
print "-" x 50, "\n";
print "\t\t\t Total:\t$subtotal\n\n";
urbansumo
quelle
Mir ist aufgefallen, dass jemand anderes dies bereits kommentiert hat. Tut mir leid, zumindest habe ich auch ein paar Beispiele, hoffentlich ergänzt es die Diskussion trotzdem.
Urbansumo