Come dividere una stringa nella shell e ottenere l'ultimo campo


293

Supponiamo di avere la stringa 1:2:3:4:5e di voler ottenere il suo ultimo campo ( 5in questo caso). Come posso farlo usando Bash? Ho provato cut, ma non so come specificare l'ultimo campo con -f.

Risposte:


430

È possibile utilizzare gli operatori di stringa :

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

Questo taglia tutto dal davanti fino a un ':', avidamente.

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
Mentre questo funziona per il problema dato, la risposta di William sotto ( stackoverflow.com/a/3163857/520162 ) restituisce anche 5se la stringa è 1:2:3:4:5:(mentre usando gli operatori di stringa si ottiene un risultato vuoto). Ciò è particolarmente utile quando si analizzano percorsi che potrebbero contenere (o meno) un /carattere finale .
Verifica il

9
Come faresti quindi l'opposto di questo? echeggiare '1: 2: 3: 4:'?
Dobz,

12
E come si mantiene la parte prima dell'ultimo separatore? Apparentemente usando ${foo%:*}. #- dall'inizio; %- dalla fine. #, %- corrispondenza più breve; ##, %%- partita più lunga.
Mihai Danila,

1
Se voglio ottenere l'ultimo elemento dal percorso, come dovrei usarlo? echo ${pwd##*/}non funziona.
Putnik,

2
@Putnik quel comando vede pwdcome una variabile. Prova dir=$(pwd); echo ${dir##*/}. Per me va bene!
Stan Strum il

344

Un altro modo è di invertire prima e dopo cut:

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

Questo rende molto facile ottenere l'ultimo ma un campo, o qualsiasi intervallo di campi numerati dalla fine.


17
Questa risposta è buona perché usa 'cut', che l'autore (presumibilmente) ha già familiarità. Inoltre, mi piace questa risposta perché sto usando 'cut' e ho avuto questa domanda esatta, quindi trovando questa discussione tramite la ricerca.
Dannid,

6
Alcuni foraggi tagliati e incollati per le persone che usano gli spazi come delimitatori:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
il rev | cut -d -f1 | il rev è così intelligente! Grazie! Mi ha aiutato un sacco (il mio caso d'uso era rev | -d '' -f 2- | rev
EdgeCaseBerg

1
Mi dimentico sempre rev, era proprio quello di cui avevo bisogno! cut -b20- | rev | cut -b10- | rev
shearn89,

Ho finito con questa soluzione, il mio tentativo di tagliare i percorsi dei file con "awk -F" / "'{print $ NF}'" si è rotto in qualche modo per me, poiché anche i nomi dei file, inclusi gli spazi bianchi, sono stati separati
THX,

76

È difficile ottenere l'ultimo campo usando cut, ma qui ci sono alcune soluzioni in awk e perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
grande vantaggio di questa soluzione rispetto alla risposta accettata: corrisponde anche a percorsi che contengono o non contengono un /carattere di finitura : /a/b/c/de /a/b/c/d/producono lo stesso risultato ( d) durante l'elaborazione pwd | awk -F/ '{print $NF}'. La risposta accettata si traduce in un risultato vuoto nel caso di/a/b/c/d/
eckes

@eckes Nel caso della soluzione AWK, su GNU bash, versione 4.3.48 (1) -creare questo non è vero, poiché importa ogni volta che hai una barra o meno. In poche parole AWK userà /come delimitatore, e se il tuo percorso è /my/path/dir/userà il valore dopo l'ultimo delimitatore, che è semplicemente una stringa vuota. Quindi è meglio evitare la barra finale se è necessario fare una cosa del genere.
Criceto

Come posso ottenere la sottostringa FINO ALL'ultimo campo?
blackjacx,

1
@blackjacx Ci sono alcune stranezze, ma qualcosa del genere awk '{$NF=""; print $0}' FS=: OFS=:spesso funziona abbastanza bene.
William Pursell,

29

Supponendo un utilizzo abbastanza semplice (nessuna evasione del delimitatore, per esempio), puoi usare grep:

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

Ripartizione: trova tutti i caratteri non il delimitatore ([^:]) alla fine della riga ($). -o stampa solo la parte corrispondente.


-E significa usare la sintassi estesa; [^ ...] significa tutto tranne i caratteri elencati; + uno o più di questi successi (prenderà la lunghezza massima possibile per il modello; questo elemento è un'estensione gnu) - per esempio i caratteri di separazione sono i due punti.
Alexander Stohr,

18

Senso unico:

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

Un altro, usando un array:

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

Ancora un altro con un array:

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

Utilizzo delle espressioni regolari di Bash (versione> = 3.2):

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

Basta tradurre il delimitatore in una nuova riga e scegliere l'ultima voce con tail -1.


Fallirà se l'ultimo elemento contiene un \n, ma per la maggior parte dei casi è la soluzione più leggibile.
Yajo,

7

Utilizzando 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


4

Ci sono molte buone risposte qui, ma voglio ancora condividere questo usando basename :

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

Tuttavia fallirà se ci sono già alcuni '/' nella tua stringa . Se slash / è il delimitatore, allora devi (e dovresti) usare il nome di base.

Non è la risposta migliore ma mostra solo come puoi essere creativo usando i comandi bash.


2

Usando Bash.

$ var1="1:2:3:4:0"
$ IFS=":"
$ set -- $var1
$ eval echo  \$${#}
0

Avrebbe potuto usare echo ${!#}invece di eval echo \$${#}.
Rafa,

2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

Per prima cosa usa xargs dividerlo usando ":", - n1 significa che ogni riga ha solo una parte. Quindi, premi l'ultima parte.


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
Ciò si verifica in presenza di spazi vuoti in uno dei campi. Inoltre, non affronta direttamente la questione del recupero dell'ultimo campo.
Chepner,

1

Per coloro che si sentono a proprio agio con Python, https://github.com/Russell91/pythonpy è una buona scelta per risolvere questo problema.

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

Dalla aiuto pythonpy: -x treat each row of stdin as x.

Con quello strumento, è facile scrivere codice Python che viene applicato all'input.


1

Potrebbe essere un po 'in ritardo con la risposta, sebbene una soluzione semplice sia quella di invertire l'ordine della stringa di input. Ciò ti consentirebbe di ottenere sempre l'ultimo oggetto, indipendentemente dalla lunghezza.

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

È importante notare, tuttavia, se si utilizza questo metodo e i numeri sono più grandi di 1 cifra (o più grandi di un carattere in qualsiasi circostanza), sarà necessario eseguire un altro comando 'rev' sull'output del piping.

[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

Spero di poterti aiutare, evviva


1

Una soluzione che utilizza l'integrato letto:

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

Oppure, per renderlo più generico:

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

0

Se ti piace python e hai un'opzione per installare un pacchetto, puoi usare questa utility python .

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

python può farlo direttamente: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB Ti sbagli. Lo scopo del pythonppacchetto è quello di farti fare le stesse cose python -ccon un minor numero di caratteri. Dai un'occhiata a README nel repository.
Bombe

0

La corrispondenza di Regex in sedè avida (va sempre all'ultima occorrenza), che puoi usare a tuo vantaggio qui:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.