So finden Sie das letzte Feld mit "Ausschneiden"

310

Ohne Verwendung sedoder awk, nur cut , wie bekomme ich das letzte Feld , wenn die Anzahl der Felder unbekannt oder zu ändern ist mit jeder Zeile?

Noobcoder
quelle
8
Bist du verliebt in cutKommando :)? Warum nicht irgendwelche anderen Linux-Befehle?
Jayesh Bhoi
7
Ohne sedoder awk: perl -pe 's/^.+\s+([^\s]+)$/$1/'.
Jordanm
3
Mögliches Duplikat, wie man einen String in eine Shell
Amir Ali Akbari,
4
@MestreLion Oft lesen Leute eine Frage, um eine Lösung für eine Variation eines Problems zu finden. Dieser beginnt mit der falschen Prämisse, cutdie etwas unterstützt, was er nicht unterstützt. Aber ich fand es nützlich, weil es den Leser dazu zwingt, Code zu betrachten, der leichter zu befolgen ist. Ich wollte eine schnelle, einfache Art und Weise zu nutzen , cutohne für mehrere Syntaxen zu verwenden , zu benötigen awk, grep, sedetc. Die revSache hat den Trick; sehr elegant und etwas, an das ich nie gedacht habe (auch wenn es für andere Situationen klobig ist). Ich habe auch gerne die anderen Ansätze aus den anderen Antworten gelesen.
Beejor
3
Kam hier ein echtes Problem: Ich möchte alle verschiedenen Dateierweiterungen in einem Quellbaum finden, um eine .gitattributes-Datei mit zu aktualisieren. So find | cut -d. -f<last>ist die natürliche Neigung
Studog

Antworten:

679

Sie könnten so etwas versuchen:

echo 'maps.google.com' | rev | cut -d'.' -f 1 | rev

Erläuterung

  • rev kehrt "maps.google.com" um moc.elgoog.spam
  • cut verwendet Punkt (dh '.') als Trennzeichen und wählt das erste Feld aus moc
  • Zuletzt kehren wir es noch einmal um, um zu bekommen com
Zedfoxus
quelle
6
Es verwendet nicht nur, cutsondern es ist ohne sedoder. awkAlso, was OP denken?
Jayesh Bhoi
7
@tom OP hat in den letzten Stunden mehr Fragen gestellt als nur diese. Aufgrund unserer Interaktionen mit dem OP wissen wir, dass awk / sed / etc. sind in seinen Hausaufgaben nicht erlaubt, aber ein Hinweis auf rev wurde nicht gemacht. Es war also einen
Versuch
4
@zfus Ich verstehe. Vielleicht möchten Sie danach noch einen kleben rev.
Tom
17
doppelt revtoll ideal!
Ford Guo
6
Genial, einfach, perfekt, danke auch für die Erklärung - nicht genug Leute, die jeden Schritt in langen Ketten von Rohrleitungsbefehlen erklären
Pete
128

Verwenden Sie eine Parametererweiterung. Dies ist viel effizienter als jede Art von externem Befehl cut(oder grep).

data=foo,bar,baz,qux
last=${data##*,}

In BashFAQ # 100 finden Sie eine Einführung in die native String-Manipulation in Bash.

Charles Duffy
quelle
3
@ErwinWessels: Weil Bash sehr langsam ist. Verwenden Sie Bash, um Pipelines auszuführen, und nicht, um Daten in großen Mengen zu verarbeiten. Ich meine, das ist großartig, wenn Sie bereits eine Textzeile in einer Shell-Variablen haben oder wenn Sie while IFS= read -ra array_var; do :;done <(cmd)ein paar Zeilen verarbeiten möchten . Aber für eine große Datei ist rev | cut | rev wahrscheinlich schneller! (Und natürlich wird awk schneller sein.)
Peter Cordes
2
@PeterCordes, awk wird für eine große Datei zwar schneller sein, aber es sind einige Eingaben erforderlich, um die Startkosten mit konstantem Faktor zu überwinden. (Es gibt auch Shells - wie ksh93 - mit einer Leistung, die näher an awk liegt, wobei die in dieser Antwort angegebene Syntax weiterhin gültig ist. Bash ist außergewöhnlich träge, aber nicht annähernd die einzige verfügbare Option.)
Charles Duffy
1
Danke @PeterCordes; Wie immer hat jedes Tool seine Anwendungsfälle.
Erwin Wessels
1
Dies ist bei weitem die schnellste und präziseste Methode, um eine einzelne Variable in einem bashSkript zu kürzen (vorausgesetzt, Sie verwenden bereits ein bashSkript). Sie müssen nichts Externes anrufen.
Ken Sharp
1
@Balmipour, ... jedoch rev ist spezifisch für was auch immer OS Sie verwenden, zur Verfügung stellt - es auf allen UNIX - Systemen nicht standardisiert ist. In der Kapitelliste finden Sie den Abschnitt POSIX zu Befehlen und Dienstprogrammen - er ist nicht vorhanden. Und ${var##prefix_pattern}ist nicht in der Tat bash-spezifische; Es ist im POSIX sh-Standard enthalten , siehe Ende von Abschnitt 2.6.2 (verknüpft). Im Gegensatz dazu revist es immer auf jeder kompatiblen Shell verfügbar.
Charles Duffy
89

Es ist nicht möglich, nur zu verwenden cut. Hier ist ein Weg mit grep:

grep -o '[^,]*$'

Ersetzen Sie das Komma durch andere Trennzeichen.

Tom
quelle
3
Um das Gegenteil zu tun und alles außer dem letzten Feld zu finden, tun Sie:grep -o '^.*,'
Ariel
2
Dies war besonders nützlich, da revin meinem Fall ein Problem mit Multibyte-Unicode-Zeichen hinzugefügt wurde.
Brice
3
Ich habe versucht, dies in MinGW zu tun, aber meine grep-Version unterstützt -o nicht. Daher habe ich verwendet, sed 's/^.*,//'wodurch alle Zeichen bis einschließlich des letzten Kommas durch eine leere Zeichenfolge ersetzt werden.
TamaMcGlinn
46

Ohne awk? ... Aber mit awk ist es so einfach:

echo 'maps.google.com' | awk -F. '{print $NF}'

AWK ist ein viel leistungsfähigeres Werkzeug, das Sie in Ihrer Tasche haben können. -F wenn für Feldtrennzeichen NF die Anzahl der Felder ist (steht auch für den Index des letzten)

Amir Mehler
quelle
2
Dies ist universell und funktioniert jedes Mal genau wie erwartet. In diesem Szenario entspricht die Verwendung cutzur Erzielung der endgültigen Ausgabe des OP der Verwendung eines Löffels zum "Schneiden" des Steaks (Wortspiel beabsichtigt :)). awkist das Steakmesser.
Hickory420
3
Vermeiden Sie unnötige Verwendung echo, da dies das Skript für lange Dateien verlangsamen kann awk -F. '{print $NF}' <<< 'maps.google.com'.
Anil_M
14

Es gibt mehrere Möglichkeiten. Sie können dies auch verwenden.

echo "Your string here"| tr ' ' '\n' | tail -n1
> here

Offensichtlich sollte die Leerzeichen-Eingabe für den Befehl tr durch das von Ihnen benötigte Trennzeichen ersetzt werden.

rjni
quelle
Danke dir! etwas, das in Busybox Sh 1.0.0 funktioniert :)
Kevin
1
Dies fühlt sich für mich wie die einfachste Antwort an, weniger Pfeifen und eine klarere Bedeutung
JoeButler
1
Das funktioniert nicht für eine ganze Datei, was das OP wahrscheinlich bedeutete.
Amir
7

Dies ist die einzig mögliche Lösung, um nur Schnitt zu verwenden:

echo "string" | cut -d '.' -f2- [repeat_following_part_forever_or_until_out_of_memory:] | cut -d '.' -f2-

Bei Verwendung dieser Lösung kann die Anzahl der Felder tatsächlich unbekannt sein und von Zeit zu Zeit variieren. Da die Zeilenlänge jedoch LINE_MAX-Zeichen oder -Felder, einschließlich des Zeichens für neue Zeilen, nicht überschreiten darf, kann eine beliebige Anzahl von Feldern als echte Bedingung dieser Lösung niemals Teil sein.

Ja, eine sehr dumme Lösung, aber die einzige, die die Kriterien erfüllt, denke ich.

Ein Freund
quelle
2
Nett. Nimm einfach das letzte '.' aus "string" und das funktioniert.
Matt
2
Ich liebe es, wenn jeder sagt, etwas sei unmöglich und dann mischt sich jemand mit einer funktionierenden Antwort ein. Auch wenn es in der Tat sehr dumm ist.
Beejor
Man könnte cut -f2-in einer Schleife iterieren , bis sich die Ausgabe nicht mehr ändert.
Loa_in_
4

Wenn Ihre Eingabezeichenfolge keine Schrägstriche enthält, können Sie basenameeine Unterschale verwenden:

$ basename "$(echo 'maps.google.com' | tr '.' '/')"

Dies wird nicht verwendet sedoder awkes wird auch nicht verwendet cut, daher bin ich mir nicht ganz sicher, ob es sich um eine Antwort auf die Frage handelt, wie sie formuliert ist.

Dies funktioniert nicht gut, wenn Eingabezeichenfolgen verarbeitet werden, die Schrägstriche enthalten können. Eine Problemumgehung für diese Situation besteht darin, den Schrägstrich durch ein anderes Zeichen zu ersetzen, von dem Sie wissen, dass es nicht Teil einer gültigen Eingabezeichenfolge ist. Beispielsweise ist das |Zeichen pipe ( ) auch in Dateinamen nicht zulässig. Dies würde also funktionieren:

$ basename "$(echo 'maps.google.com/some/url/things' | tr '/' '|' | tr '.' '/')" | tr '|' '/'
jstine
quelle
2

Das Folgende implementiert den Vorschlag eines Freundes

#!/bin/bash
rcut(){

  nu="$( echo $1 | cut -d"$DELIM" -f 2-  )"
  if [ "$nu" != "$1" ]
  then
    rcut "$nu"
  else
    echo "$nu"
  fi
}

$ export DELIM=.
$ rcut a.b.c.d
d
user2166700
quelle
2
Sie benötigen doppelte Anführungszeichen um die Argumente, echodamit dies zuverlässig und robust funktioniert. Siehe stackoverflow.com/questions/10067266/…
Tripleee
0

Wenn Sie eine Datei mit dem Namen filelist.txt haben, bei der es sich um Listenpfade wie die folgenden handelt: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h

dann können Sie dies tun: rev filelist.txt | cut -d "/" -f1 | rev

aperson1961
quelle
0

Hinzufügen eines Ansatzes zu dieser alten Frage nur zum Spaß:

$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info

$ cat tmp.sh # showing off the script to do the job
#!/bin/bash
delim=';'
while read -r line; do  
    while [[ "$line" =~ "$delim" ]]; do
        line=$(cut -d"$delim" -f 2- <<<"$line")
    done
    echo "$line"
done < input.file

$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info

Neben Bash wird nur Cut verwendet. Nun, und Echo, denke ich.

Kaffe Myers
quelle
Meh, warum nicht einfach den Schnitt komplett entfernen und nur bash verwenden ... x] while read -r line; do echo ${line/*;}; done <input.fileergibt das gleiche Ergebnis.
Kaffe Myers
-1

Mir wurde klar, dass es funktioniert, wenn wir nur sicherstellen, dass ein abschließendes Trennzeichen vorhanden ist. In meinem Fall habe ich also Komma- und Leerzeichen. Ich füge am Ende ein Leerzeichen hinzu.

$ ans="a, b"
$ ans+=" "; echo ${ans} | tr ',' ' ' | tr -s ' ' | cut -d' ' -f2
b
AnneTheAgile
quelle
Und ans="a, b, c"produziert b, was nicht den Anforderungen von "Anzahl der Felder ist unbekannt oder ändert sich mit jeder Zeile" entspricht .
JWW