So teilen Sie eine Zeichenfolge in eine Shell und erhalten das letzte Feld

293

Angenommen, ich habe die Zeichenfolge 1:2:3:4:5und möchte das letzte Feld ( 5in diesem Fall) abrufen. Wie mache ich das mit Bash? Ich habe es versucht cut, aber ich weiß nicht, wie ich das letzte Feld mit angeben soll -f.

cd1
quelle

Antworten:

430

Sie können Zeichenfolgenoperatoren verwenden :

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

Dies schneidet alles von vorne bis zu einem ':' gierig ab.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }
Stephen
quelle
7
Während dies für das gegebene Problem funktioniert, gibt die Antwort von William unten ( stackoverflow.com/a/3163857/520162 ) auch zurück, 5wenn die Zeichenfolge ist 1:2:3:4:5:(während die Verwendung der Zeichenfolgenoperatoren ein leeres Ergebnis ergibt). Dies ist besonders praktisch, wenn Pfade analysiert werden, die ein Abschlusszeichen enthalten können (oder nicht) /.
eckes
9
Wie würden Sie dann das Gegenteil davon tun? '1: 2: 3: 4:' wiedergeben?
Dobz
12
Und wie hält man das Teil vor dem letzten Trennzeichen? Anscheinend mit ${foo%:*}. #- von Anfang an; %- vom Ende. #, %- kürzeste Übereinstimmung; ##, %%- längste Übereinstimmung.
Mihai Danila
1
Wenn ich das letzte Element vom Pfad erhalten möchte, wie soll ich es verwenden? echo ${pwd##*/}funktioniert nicht.
Putnik
2
@Putnik, den dieser Befehl pwdals Variable sieht . Versuchen Sie es dir=$(pwd); echo ${dir##*/}. Funktioniert bei mir!
Stan Strum
344

Eine andere Möglichkeit besteht darin, vorher und nachher umzukehren cut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

Dies macht es sehr einfach, das vorletzte Feld oder einen beliebigen Bereich von Feldern zu erhalten, die vom Ende an nummeriert sind.

a3nm
quelle
17
Diese Antwort ist nett, weil sie 'cut' verwendet, was dem Autor (vermutlich) bereits bekannt ist. Außerdem gefällt mir diese Antwort, weil ich 'cut' verwende und genau diese Frage hatte, sodass ich diesen Thread über die Suche finde.
Dannid
6
Einige Cut-and-Paste-Futter für Menschen, die Leerzeichen als Trennzeichen verwenden:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
Funroll
2
die rev | cut -d -f1 | rev ist so schlau! Vielen Dank! Hat mir ein paar geholfen (mein Anwendungsfall war rev | -d '' -f 2- | rev
EdgeCaseBerg
1
Ich vergesse immer rev, war genau das, was ich brauchte! cut -b20- | rev | cut -b10- | rev
Shearn89
Am Ende hatte ich diese Lösung. Mein Versuch, Dateipfade mit "awk -F" / "'{print $ NF}'" zu schneiden, brach für mich etwas ab, da Dateinamen einschließlich Leerzeichen ebenfalls auseinandergeschnitten wurden
THX
76

Es ist schwierig, das letzte Feld mit cut zu erhalten, aber hier sind einige Lösungen in awk und perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'
William Pursell
quelle
5
Ein großer Vorteil dieser Lösung gegenüber der akzeptierten Antwort: Sie stimmt auch mit Pfaden überein, die ein Abschlusszeichen enthalten oder nicht /: /a/b/c/dund /a/b/c/d/liefert dbei der Verarbeitung das gleiche Ergebnis ( ) pwd | awk -F/ '{print $NF}'. Die akzeptierte Antwort führt zu einem leeren Ergebnis im Fall von/a/b/c/d/
eckes
@eckes Im Falle einer AWK-Lösung ist dies bei GNU Bash, Version 4.3.48 (1), nicht der Fall, da dies wichtig ist, wenn Sie einen abschließenden Schrägstrich haben oder nicht. Einfach ausgedrückt, AWK wird /als Trennzeichen verwendet, und wenn Ihr Pfad ist /my/path/dir/, wird der Wert nach dem letzten Trennzeichen verwendet, bei dem es sich einfach um eine leere Zeichenfolge handelt. Vermeiden Sie daher am besten einen nachgestellten Schrägstrich, wenn Sie so etwas wie ich tun müssen.
Stamster
Wie würde ich den Teilstring BIS zum letzten Feld bekommen?
Blackjacx
1
@blackjacx Es gibt einige Macken, aber so etwas awk '{$NF=""; print $0}' FS=: OFS=:funktioniert oft gut genug.
William Pursell
29

Unter der Annahme einer relativ einfachen Verwendung (z. B. kein Entkommen des Trennzeichens) können Sie grep verwenden:

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

Aufschlüsselung - Suchen Sie alle Zeichen, nicht das Trennzeichen ([^:]) am Ende der Zeile ($). -o druckt nur das passende Teil.

Nicholas MT Elliott
quelle
-E bedeutet die Verwendung einer erweiterten Syntax; [^ ...] bedeutet alles andere als die aufgelisteten Zeichen; + ein oder mehrere solcher Treffer (nimmt die maximal mögliche Länge für das Muster an; dieser Gegenstand ist eine Gnu-Erweiterung) - für das Beispiel sind die Trennzeichen der Doppelpunkt.
Alexander Stohr
18

Einweg:

var1="1:2:3:4:5"
var2=${var1##*:}

Ein anderer, der ein Array verwendet:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

Noch eine mit einem Array:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

Verwenden von regulären Bash-Ausdrücken (Version> = 3.2):

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}
Bis auf weiteres angehalten.
quelle
11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Übersetzen Sie einfach das Trennzeichen in eine neue Zeile und wählen Sie den letzten Eintrag mit tail -1.

user3133260
quelle
Es schlägt fehl, wenn das letzte Element ein enthält \n, aber in den meisten Fällen ist es die am besten lesbare Lösung.
Yajo
7

Verwenden von sed:

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c
Rafael
quelle
4

Wenn Ihr letztes Feld ein einzelnes Zeichen ist, können Sie Folgendes tun:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

Überprüfen Sie die String-Manipulation in Bash .

Ab Irato
quelle
Dies funktioniert nicht: Es gibt das letzte Zeichen von a, nicht das letzte Feld .
gniourf_gniourf
1
Das ist die Idee, wenn Sie die Länge des letzten Feldes kennen, ist es gut. Wenn nicht, müssen Sie etwas anderes verwenden ...
Ab Irato
4

Hier gibt es viele gute Antworten, aber ich möchte diese trotzdem mit dem Basisnamen teilen :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

Es schlägt jedoch fehl, wenn Ihre Zeichenfolge bereits ein '/' enthält . Wenn Schrägstrich / Ihr Trennzeichen ist, müssen (und sollten) Sie nur den Basisnamen verwenden.

Es ist nicht die beste Antwort, aber es zeigt nur, wie Sie mit Bash-Befehlen kreativ sein können.

021
quelle
2

Verwenden von Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0
Ghostdog74
quelle
Könnte echo ${!#}anstelle von verwendet haben eval echo \$${#}.
Rafa
2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

Verwenden Sie zuerst xargs und teilen Sie es mit ":". - n1 bedeutet, dass jede Zeile nur einen Teil hat. Dann drücken Sie den letzten Teil.

Crytis
quelle
1
for x in `echo $str | tr ";" "\n"`; do echo $x; done
Nahid Akbar
quelle
2
Dies führt zu Problemen, wenn in einem der Felder Leerzeichen vorhanden sind. Außerdem wird die Frage des Abrufs des letzten Felds nicht direkt angesprochen .
Chepner
1

Für diejenigen, die mit Python vertraut sind, ist https://github.com/Russell91/pythonpy eine gute Wahl, um dieses Problem zu lösen.

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

Aus der Pythonpy-Hilfe : -x treat each row of stdin as x.

Mit diesem Tool ist es einfach, Python-Code zu schreiben, der auf die Eingabe angewendet wird.

Christoph Böddeker
quelle
1

Die Antwort könnte etwas spät sein, obwohl eine einfache Lösung darin besteht, die Reihenfolge der Eingabezeichenfolge umzukehren. Auf diese Weise können Sie unabhängig von der Länge immer den letzten Gegenstand erhalten.

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

Es ist jedoch wichtig zu beachten, dass Sie bei Verwendung dieser Methode und wenn die Zahlen größer als eine Ziffer sind (oder unter allen Umständen größer als ein Zeichen), einen weiteren 'rev'-Befehl über die Pipe-Ausgabe ausführen müssen.

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

Hoffe ich kann helfen, Prost

Chris
quelle
1

Eine Lösung mit dem eingebauten Read:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

Oder um es allgemeiner zu machen:

echo "${fields[-1]}" # prints the last item
baz
quelle
0

Wenn Sie Python mögen und die Option haben, ein Paket zu installieren, können Sie dieses Python-Dienstprogramm verwenden .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5
Bomben
quelle
Python kann dies direkt tun: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB
@MortenB Sie irren sich. Der gesamte Zweck des pythonpPakets besteht darin, Sie dazu zu bringen, die gleichen Dinge wie python -cmit weniger Zeichentypen zu tun. Bitte werfen Sie einen Blick auf die README im Repository.
Bomben
0

Regex Matching in sedist gierig (geht immer bis zum letzten Vorkommen), was Sie hier zu Ihrem Vorteil nutzen können:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
matschig
quelle