Cómo dividir una cadena en shell y obtener el último campo


293

Supongamos que tengo la cadena 1:2:3:4:5y quiero obtener su último campo ( 5en este caso). ¿Cómo hago eso usando Bash? Lo intenté cut, pero no sé cómo especificar el último campo con -f.

Respuestas:


430

Puede usar operadores de cadena :

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

Esto recorta todo desde el frente hasta un ':', con avidez.

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

77
Si bien esto funciona para el problema dado, la respuesta de William a continuación ( stackoverflow.com/a/3163857/520162 ) también se devuelve 5si la cadena lo es 1:2:3:4:5:(al usar los operadores de cadena se obtiene un resultado vacío). Esto es especialmente útil al analizar rutas que podrían contener (o no) un /carácter final .
Eckes

99
¿Cómo harías lo contrario de esto? hacer eco de '1: 2: 3: 4:'?
Dobz

12
¿Y cómo se mantiene la parte antes del último separador? Aparentemente usando ${foo%:*}. #- desde el principio; %- desde el final. #, %- partido más corto; ##, %%- partido más largo.
Mihai Danila

1
Si quiero obtener el último elemento de la ruta, ¿cómo debo usarlo? echo ${pwd##*/}No funciona.
Putnik

2
@Putnik ese comando ve pwdcomo una variable. Tratar dir=$(pwd); echo ${dir##*/}. ¡Funciona para mi!
Stan Strum

344

Otra forma es revertir antes y después cut:

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

Esto hace que sea muy fácil obtener el último pero un campo, o cualquier rango de campos numerados desde el final.


17
Esta respuesta es buena porque usa 'cortar', que el autor (presumiblemente) ya conoce. Además, me gusta esta respuesta porque yo estoy usando 'corte' y tenía esta pregunta exacta, por lo tanto, la búsqueda de este hilo a través de la búsqueda.
Dannid

66
Algunos forrajes de cortar y pegar para personas que usan espacios como delimitadores:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
la rev | cortar -d -f1 | ¡rev es tan inteligente! ¡Gracias! Me ayudó mucho (mi caso de uso fue rev | -d '' -f 2- | rev
EdgeCaseBerg

1
¡Siempre me olvido de que revera justo lo que necesitaba! cut -b20- | rev | cut -b10- | rev
shearn89

Terminé con esta solución, mi intento de cortar las rutas de archivo con "awk -F" / "'{print $ NF}'" se rompió un poco para mí, ya que los nombres de archivo que incluyen espacios en blanco también se cortaron
THX

76

Es difícil obtener el último campo usando cortar, pero aquí hay algunas soluciones en awk y perl

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

55
Gran ventaja de esta solución sobre la respuesta aceptada: también coincide con las rutas que contienen o no contienen un /carácter final : /a/b/c/dy /a/b/c/d/producen el mismo resultado ( d) al procesar pwd | awk -F/ '{print $NF}'. La respuesta aceptada da como resultado un resultado vacío en el caso de/a/b/c/d/
eckes

@eckes En el caso de la solución AWK, en GNU bash, versión 4.3.48 (1), la liberación no es cierta, ya que es importante siempre que tenga una barra inclinada final o no. Simplemente, use AWK /como delimitador, y si su ruta es /my/path/dir/, usará el valor después del último delimitador, que es simplemente una cadena vacía. Por lo tanto, es mejor evitar la barra diagonal final si necesita hacer algo como yo.
stamster

¿Cómo obtendría la subcadena HASTA el último campo?
blackjacx

1
@blackjacx Hay algunas peculiaridades, pero algo así a awk '{$NF=""; print $0}' FS=: OFS=:menudo funciona lo suficientemente bien.
William Pursell

29

Suponiendo un uso bastante simple (sin escapar del delimitador, por ejemplo), puede usar grep:

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

Desglose: encuentre todos los caracteres, no el delimitador ([^:]) al final de la línea ($). -o solo imprime la parte correspondiente.


-E significa usar sintaxis extendida; [^ ...] significa cualquier cosa menos los caracteres enumerados; + uno o más de esos golpes (tomará la longitud máxima posible para el patrón; este elemento es una extensión ñu); por ejemplo, los caracteres separadores son los dos puntos.
Alexander Stohr

18

De una sola mano:

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

Otro, usando una matriz:

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

Otro más con una matriz:

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

Usando expresiones regulares Bash (versión> = 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

Simplemente traduzca el delimitador a una nueva línea y elija la última entrada con tail -1.


Fallará si el último elemento contiene un \n, pero para la mayoría de los casos es la solución más fácil de leer.
Yajo

7

Utilizando 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

Si su último campo es un solo carácter, puede hacer esto:

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

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

Verifique la manipulación de cadenas en bash .


Esto no funciona: da el último carácter de a, no el último campo .
gniourf_gniourf

1
Es cierto, esa es la idea, si sabes la longitud del último campo, es bueno. Si no, tienes que usar algo más ...
Ab Irato

4

Aquí hay muchas buenas respuestas, pero aún quiero compartir esta usando basename :

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

Sin embargo , fallará si ya hay algunos '/' en su cadena . Si slash / es su delimitador, entonces solo tiene que (y debería) usar basename.

No es la mejor respuesta, pero solo muestra cómo puedes ser creativo usando los comandos bash.


2

Usando Bash.

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

Podría haber usado en echo ${!#}lugar de eval echo \$${#}.
Rafa

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

Primero use xargs dividiéndolo usando ":", - n1 significa que cada línea solo tiene una parte. Luego, presione la última parte.


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

2
Esto tiene problemas si hay espacios en blanco en cualquiera de los campos. Además, no aborda directamente la cuestión de recuperar el último campo.
chepner

1

Para aquellos que se sienten cómodos con Python, https://github.com/Russell91/pythonpy es una buena opción para resolver este problema.

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

De la ayuda pythonpy: -x treat each row of stdin as x.

Con esa herramienta, es fácil escribir código python que se aplica a la entrada.


1

Puede ser un poco tarde con la respuesta, aunque una solución simple es invertir el orden de la cadena de entrada. Esto le permitiría obtener siempre el último elemento sin importar la longitud.

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

Sin embargo, es importante tener en cuenta que si usa este método y los números tienen más de 1 dígito (o más de un carácter en cualquier circunstancia), deberá ejecutar otro comando 'rev' sobre la salida canalizada.

[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

Espero poder ayudar, salud


1

Una solución usando la lectura incorporada:

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

O, para hacerlo más genérico:

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

0

Si le gusta python y tiene una opción para instalar un paquete, puede usar esta utilidad python .

# install pythonp
pythonp -m pip install pythonp

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

Python puede hacer esto directamente: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB Estás equivocado. Todo el propósito del pythonppaquete es hacer que hagas lo mismo que python -ccon menos tipos de caracteres. Eche un vistazo al archivo README en el repositorio.
Bombas

0

La coincidencia de expresiones regulares en sedes codiciosa (siempre va a la última aparición), que puede utilizar para su ventaja aquí:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.