Por solo bash , la respuesta de Kevin Little propone lo simple ${!#}. Pruébalo usando bash -c 'echo ${!#}' arg1 arg2 arg3. Para bash , ksh y zsh , la respuesta de Dennis Williamson propone ${@: -1}. Además ${*: -1}también se puede utilizar. Pruébalo usando zsh -c 'echo ${*: -1}' arg1 arg2 arg3. Pero eso no funciona para dash , csh y tcsh .
olibre
2
${!#}, a diferencia ${@: -1}, también funciona con la expansión de parámetros. Puedes probarlo con bash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out.
Arch Stanton
Respuestas:
177
Esto es un poco hack:
for last;do true;done
echo $last
Este también es bastante portátil (nuevamente, debería funcionar con bash, ksh y sh) y no cambia los argumentos, lo que podría ser bueno.
Utiliza el hecho de que forimplícitamente recorre los argumentos si no le dice qué hacer, y el hecho de que las variables de bucle no tienen un alcance: mantienen el último valor en el que se establecieron.
Para aquellos (como yo) que se preguntan por qué se necesita el espacio, man bash tiene esto que decir al respecto:> Tenga en cuenta que un desplazamiento negativo debe estar separado del colon por al menos un espacio para evitar confundirse con: expansión.
foo
1
He estado usando esto y se rompe en MSYS2 bash solo en Windows. Extraño.
Steven Lu
2
Nota: Esta respuesta funciona para todos los arreglos Bash, a diferencia de lo ${@:$#}que solo funciona $@. Si tuviera que copiar $@a una nueva matriz con arr=("$@"), ${arr[@]:$#}sería indefinido. Esto se debe a que $@tiene un elemento 0 que no está incluido en las "$@"expansiones.
Sr. Llama
@ Mr.Llama: Otro lugar que debe evitarse $#es al iterar sobre matrices, ya que, en Bash, las matrices son escasas y, si bien $#muestran el número de elementos en la matriz, no necesariamente apunta al último elemento (o elemento + 1). En otras palabras, uno no debe hacer for ((i = 0; i++; i < $#)); do something "${array[$i]}"; doney en su lugar hacer for element in "${array[@]}"; do something "$element"; doneo iterar sobre los índices: for index in "${!array[@]}"; do something "$index" "${array[$index]}"; donesi necesita hacer algo con los valores de los índices.
Pausado hasta nuevo aviso.
80
$ set quick brown fox jumps
$ echo ${*:-1:1}# last argument
jumps
$ echo ${*:-1}# or simply
jumps
$ echo ${*:-2:1}# next to last
fox
El espacio es necesario para que no se interprete como un valor predeterminado .
La mejor respuesta, ya que también incluye el penúltimo argumento. ¡Gracias!
e40
1
Steven, no sé qué hiciste para aterrizar en el Penalty Box, pero me encanta tu trabajo aquí.
Bruno Bronosky
44
si. simplemente lo mejor. todo menos comando y último argumento ${@: 1:$#-1}
Dyno Fu
1
@DynoFu gracias por eso, respondiste mi siguiente pregunta. Entonces, un cambio podría verse así: echo ${@: -1} ${@: 1:$#-1}donde el último se convierte en el primero y el resto se desliza hacia abajo
Mike
73
La respuesta más simple para bash 3.0 o superior es
_last=${!#}# *indirect reference* to the $# variable# or
_last=$BASH_ARGV # official built-in (but takes more typing :)
Eso es.
$ cat lastarg
#!/bin/bash# echo the last arg given:
_last=${!#}
echo $_last
_last=$BASH_ARGV
echo $_last
for x;do
echo $x
done
$BASH_ARGVno funciona dentro de una función bash (a menos que esté haciendo algo mal).
Big McLargeHuge
1
BASH_ARGV tiene los argumentos cuando se llamó a bash (o para una función), no la lista actual de argumentos posicionales.
Isaac
Tenga en cuenta también que lo que BASH_ARGVle dará es el valor que fue el último argumento que se le dio, en lugar de simplemente "el último valor". Por ejemplo !: si proporciona un solo argumento, entonces llama a shift, ${@:$#}no producirá nada (¡porque cambió el único argumento!), Sin embargo, BASH_ARGVtodavía le dará ese (anteriormente) último argumento.
Steven Lu
30
Utilice la indexación combinada con la longitud de:
Lo siguiente funcionará para usted. La @ es para una variedad de argumentos. : significa en. $ # es la longitud de la matriz de argumentos. Entonces el resultado es el último elemento:
${@:$#}
Ejemplo:
function afunction{
echo ${@:$#} }
afunction -d -o local50#Outputs 50
Si bien el ejemplo es para una función, los scripts también funcionan de la misma manera. Me gusta esta respuesta porque es clara y concisa.
Jonah Braun
1
Y no es hacky. Utiliza características explícitas del lenguaje, no efectos secundarios o qwerks especiales. Esta debería ser la respuesta aceptada.
musicin3d
25
Encontré esto cuando buscaba separar el último argumento de todos los anteriores. Si bien algunas de las respuestas obtienen el último argumento, no son de mucha ayuda si necesita todos los otros argumentos también. Esto funciona mucho mejor:
La respuesta de Steven Penny es un poco más agradable: usar ${@: -1}para el último y ${@: -2:1}para el segundo último (y así sucesivamente ...). Ejemplo: bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6impresiones 6. Para seguir con este enfoque actual de AgileZebra, use ${@:$#-1:1}para obtener el segundo último . Ejemplo: bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6impresiones 5. (y ${@:$#-2:1}para obtener el último último y así sucesivamente ...)
olibre
44
La respuesta de AgileZebra proporciona una forma de obtener todos menos los últimos argumentos, por lo que no diría que la respuesta de Steven la reemplaza. Sin embargo, parece que no hay razón para usar $((...))para restar el 1, simplemente puede usar ${@:1:$# - 1}.
dkasak
Gracias dkasak Actualizado para reflejar su simplificación.
AgileZebra
22
Esto funciona en todos los shells compatibles con POSIX:
Ver este comentario adjunto a una respuesta idéntica anterior.
Pausado hasta nuevo aviso.
1
La solución portátil más simple que veo aquí. Este no tiene ningún problema de seguridad, @DennisWilliamson, la cita empírica parece ser correcta, a menos que haya una manera de establecer $#una cadena arbitraria (no lo creo). eval last=\"\${$#}\"También funciona y es obviamente correcto. No veo por qué las citas no son necesarias.
Palec
1
Para bash , zsh , dash y ksheval last=\"\${$#}\" está bien. Pero para csh y tcsh uso eval set last=\"\${$#}\". Vea este ejemplo: tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3.
olibre
12
Aquí está la solución mía:
bastante portátil (todos los POSIX sh, bash, ksh, zsh) deberían funcionar
no cambia los argumentos originales (cambia una copia).
Gran respuesta: corta, portátil, segura. ¡Gracias!
Ján Lalinský
2
Esta es una gran idea, pero tengo un par de sugerencias: en primer lugar, las citas deben agregarse tanto alrededor "$1"como "$#"(vea esta gran respuesta unix.stackexchange.com/a/171347 ). En segundo lugar, echolamentablemente no es portátil (especialmente para -n), por lo que printf '%s\n' "$1"debe usarse en su lugar.
joki
gracias @joki Trabajé con muchos sistemas unix diferentes y tampoco confiaría en echo -nellos, sin embargo, no estoy al tanto de ningún sistema posix donde echo "$1"fallara. De todos modos, printfes más predecible, actualizado.
Michał Šrajer
@ MichałŠrajer considere el caso donde "$ 1" es "-n" o "-", por ejemplo, ntharg 1 -no ntharg 1 --puede producir resultados diferentes en varios sistemas. ¡El código que tienes ahora es seguro!
joki
10
De las soluciones más antiguas a las más nuevas:
La solución más portátil, incluso más antigua sh(funciona con espacios y caracteres globales) (sin bucle, más rápido):
eval printf "'%s\n'""\"\${$#}\""
Desde la versión 2.01 de bash
$ set--The quick brown fox jumps over the lazy dog
$ printf '%s\n'"${!#} ${@:(-1)} ${@: -1} ${@:~0} ${!#}"
dog dog dog dog dog
Para ksh, zsh y bash:
$ printf '%s\n'"${@: -1} ${@:~0}"# the space beetwen `:`# and `-1` is a must.
dog dog
Y para "penúltimo":
$ printf '%s\n'"${@:~1:1}"
lazy
Usar printf para solucionar cualquier problema con argumentos que comienzan con un guión (como -n ).
Para todos los shells y para mayores sh(funciona con espacios y caracteres glob) es:
$ set--The quick brown fox jumps over the lazy dog "the * last argument"
$ eval printf "'%s\n'""\"\${$#}\""The last * argument
O, si desea establecer una lastvar:
$ eval last=\${$#}; printf '%s\n' "$last"The last * argument
shift $(($# - 1))- No necesita una utilidad externa. Funciona en Bash, ksh, zsh y dash.
Pausado hasta nuevo aviso.
@ Dennis: ¡Qué bien! No sabía sobre la $((...))sintaxis.
Laurence Gonsalves
Desea utilizar printf '%s\n' "$1"para evitar un comportamiento inesperado de echo(por ejemplo, para -n).
joki
2
Si desea hacerlo de una manera no destructiva, una forma es pasar todos los argumentos a una función y devolver el último:
#!/bin/bash
last(){if[[ $# -ne 0 ]] ; then
shift $(expr $# - 1)
echo "$1"#else#do something when no argumentsfi}
lastvar=$(last "$@")
echo $lastvar
echo "$@"
pax>./qq.sh 123 a b
b
123 a b
Si realmente no te importa mantener los otros argumentos, no lo necesitas en una función, pero me cuesta pensar en una situación en la que nunca quieras conservar los otros argumentos a menos que ya hayan sido procesados, en cuyo caso usaría el método process / shift / process / shift / ... para procesarlos secuencialmente.
Asumo aquí que desea conservarlos porque no ha seguido el método secuencial. Este método también maneja el caso donde no hay argumentos, devolviendo "". Puede ajustar fácilmente ese comportamiento insertando la elsecláusula comentada .
Sé que publicaste esto hace mucho tiempo, pero esta solución es genial, ¡me alegra que alguien haya publicado una tcsh!
usuario3295674
2
Encontré la respuesta de @ AgileZebra (más el comentario de @ starfry) la más útil, pero se establece headsen un escalar. Una matriz es probablemente más útil:
evaluar para referencia indirecta es exagerado, sin mencionar las malas prácticas, y una gran preocupación de seguridad (el valor no está entre comillas ni fuera de $ (). Bash tiene una sintaxis incorporada para referencias indirectas, para cualquier var: !entonces last="${!#}"usaría el mismo . enfoque (referencia indirecta en $ #) en un lugar más seguro, compacto, incorporado, de manera mucho sano y adecuadamente citado.
MestreLion
Vea una forma más limpia de realizar lo mismo en otra respuesta .
Palec
Las cotizaciones de @MestreLion no son necesarias en el RHS de =.
Tom Hale
1
@TomHale: verdadero para el caso particular de ${!#}, pero no en general: todavía se necesitan comillas si el contenido contiene espacios en blanco literales, como last='two words'. Solo $()es seguro para espacios en blanco independientemente del contenido.
MestreLion
1
Después de leer las respuestas anteriores, escribí un script de shell Q&D (debería funcionar en sh y bash) para ejecutar g ++ en PGM.cpp para producir una imagen ejecutable PGM. Se supone que el último argumento en la línea de comando es el nombre del archivo (.cpp es opcional) y todos los demás argumentos son opciones.
#!/bin/shif[ $# -lt 1 ]then
echo "Usage: `basename $0` [opt] pgm runs g++ to compile pgm[.cpp] into pgm"
exit 2fi
OPT=
PGM=# PGM is the last argument, all others are considered optionsfor F;do OPT="$OPT $PGM"; PGM=$F;done
DIR=`dirname $PGM`
PGM=`basename $PGM .cpp`# put -o first so it can be overridden by -o specified in OPTset-x
g++-o $DIR/$PGM $OPT $DIR/$PGM.cpp
El enfoque eval ya se ha presentado aquí muchas veces, pero este tiene una explicación de cómo funciona. Podría mejorarse aún más, pero aún vale la pena conservarlo.
Sospecho que esto podría fallar con ./arguments.sh "último valor"
Thomas
Gracias por revisar a Thomas, he intentado ejecutar el script como me mencionó # ./arguments.sh "last value" Last Argument is: value está funcionando bien ahora. # ./arguments.sh "verificación del último valor con doble" El último argumento es: doble
Ranjithkumar T
El problema es que el último argumento fue 'último valor' y no valor. El error es causado por el argumento que contiene un espacio.
Thomas
0
Hay una manera mucho más concisa de hacer esto. Los argumentos de una secuencia de comandos bash se pueden incorporar a una matriz, lo que simplifica mucho el manejo de los elementos. El siguiente script siempre imprimirá el último argumento pasado a un script.
argArray=("$@")# Add all script arguments to argArray
arrayLength=${#argArray[@]}# Get the length of the array
lastArg=$((arrayLength -1))# Arrays are zero based, so last arg is -1
echo ${argArray[$lastArg]}
${!#}
. Pruébalo usandobash -c 'echo ${!#}' arg1 arg2 arg3
. Para bash , ksh y zsh , la respuesta de Dennis Williamson propone${@: -1}
. Además${*: -1}
también se puede utilizar. Pruébalo usandozsh -c 'echo ${*: -1}' arg1 arg2 arg3
. Pero eso no funciona para dash , csh y tcsh .${!#}
, a diferencia${@: -1}
, también funciona con la expansión de parámetros. Puedes probarlo conbash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out
.Respuestas:
Esto es un poco hack:
Este también es bastante portátil (nuevamente, debería funcionar con bash, ksh y sh) y no cambia los argumentos, lo que podría ser bueno.
Utiliza el hecho de que
for
implícitamente recorre los argumentos si no le dice qué hacer, y el hecho de que las variables de bucle no tienen un alcance: mantienen el último valor en el que se establecieron.fuente
true
es parte de POSIX .for last in "$@"; do :; done
también hace que la intención sea mucho más clara.Esto es solo Bash:
fuente
${@:$#}
que solo funciona$@
. Si tuviera que copiar$@
a una nueva matriz conarr=("$@")
,${arr[@]:$#}
sería indefinido. Esto se debe a que$@
tiene un elemento 0 que no está incluido en las"$@"
expansiones.$#
es al iterar sobre matrices, ya que, en Bash, las matrices son escasas y, si bien$#
muestran el número de elementos en la matriz, no necesariamente apunta al último elemento (o elemento + 1). En otras palabras, uno no debe hacerfor ((i = 0; i++; i < $#)); do something "${array[$i]}"; done
y en su lugar hacerfor element in "${array[@]}"; do something "$element"; done
o iterar sobre los índices:for index in "${!array[@]}"; do something "$index" "${array[$index]}"; done
si necesita hacer algo con los valores de los índices.El espacio es necesario para que no se interprete como un valor predeterminado .
Tenga en cuenta que esto es solo bash.
fuente
${@: 1:$#-1}
echo ${@: -1} ${@: 1:$#-1}
donde el último se convierte en el primero y el resto se desliza hacia abajoLa respuesta más simple para bash 3.0 o superior es
Eso es.
Salida es:
fuente
$BASH_ARGV
no funciona dentro de una función bash (a menos que esté haciendo algo mal).BASH_ARGV
le dará es el valor que fue el último argumento que se le dio, en lugar de simplemente "el último valor". Por ejemplo !: si proporciona un solo argumento, entonces llama a shift,${@:$#}
no producirá nada (¡porque cambió el único argumento!), Sin embargo,BASH_ARGV
todavía le dará ese (anteriormente) último argumento.Utilice la indexación combinada con la longitud de:
Tenga en cuenta que esto es solo bash.
fuente
echo ${@:$#}
Lo siguiente funcionará para usted. La @ es para una variedad de argumentos. : significa en. $ # es la longitud de la matriz de argumentos. Entonces el resultado es el último elemento:
Ejemplo:
Tenga en cuenta que esto es solo bash.
fuente
Encontré esto cuando buscaba separar el último argumento de todos los anteriores. Si bien algunas de las respuestas obtienen el último argumento, no son de mucha ayuda si necesita todos los otros argumentos también. Esto funciona mucho mejor:
Tenga en cuenta que esto es solo bash.
fuente
${@: -1}
para el último y${@: -2:1}
para el segundo último (y así sucesivamente ...). Ejemplo:bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6
impresiones6
. Para seguir con este enfoque actual de AgileZebra, use${@:$#-1:1}
para obtener el segundo último . Ejemplo:bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6
impresiones5
. (y${@:$#-2:1}
para obtener el último último y así sucesivamente ...)$((...))
para restar el 1, simplemente puede usar${@:1:$# - 1}
.Esto funciona en todos los shells compatibles con POSIX:
Fuente: http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html
fuente
$#
una cadena arbitraria (no lo creo).eval last=\"\${$#}\"
También funciona y es obviamente correcto. No veo por qué las citas no son necesarias.eval last=\"\${$#}\"
está bien. Pero para csh y tcsh usoeval set last=\"\${$#}\"
. Vea este ejemplo:tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3
.Aquí está la solución mía:
maleval
Código:
fuente
"$1"
como"$#"
(vea esta gran respuesta unix.stackexchange.com/a/171347 ). En segundo lugar,echo
lamentablemente no es portátil (especialmente para-n
), por lo queprintf '%s\n' "$1"
debe usarse en su lugar.echo -n
ellos, sin embargo, no estoy al tanto de ningún sistema posix dondeecho "$1"
fallara. De todos modos,printf
es más predecible, actualizado.ntharg 1 -n
ontharg 1 --
puede producir resultados diferentes en varios sistemas. ¡El código que tienes ahora es seguro!De las soluciones más antiguas a las más nuevas:
La solución más portátil, incluso más antigua
sh
(funciona con espacios y caracteres globales) (sin bucle, más rápido):Desde la versión 2.01 de bash
Para ksh, zsh y bash:
Y para "penúltimo":
Usar printf para solucionar cualquier problema con argumentos que comienzan con un guión (como
-n
).Para todos los shells y para mayores
sh
(funciona con espacios y caracteres glob) es:O, si desea establecer una
last
var:Y para "penúltimo":
fuente
Si está utilizando Bash> = 3.0
fuente
echo ${BASH_ARGV[1]}
ARGV
significa "vector de argumento".Para
bash
, este comentario sugirió lo muy elegante:Como beneficio adicional, esto también funciona en
zsh
.fuente
Esto cambia los argumentos por el número de argumentos menos 1, y devuelve el primer (y único) argumento restante, que será el último.
Solo probé en bash, pero también debería funcionar en sh y ksh.
fuente
shift $(($# - 1))
- No necesita una utilidad externa. Funciona en Bash, ksh, zsh y dash.$((...))
sintaxis.printf '%s\n' "$1"
para evitar un comportamiento inesperado deecho
(por ejemplo, para-n
).Si desea hacerlo de una manera no destructiva, una forma es pasar todos los argumentos a una función y devolver el último:
Si realmente no te importa mantener los otros argumentos, no lo necesitas en una función, pero me cuesta pensar en una situación en la que nunca quieras conservar los otros argumentos a menos que ya hayan sido procesados, en cuyo caso usaría el método process / shift / process / shift / ... para procesarlos secuencialmente.
Asumo aquí que desea conservarlos porque no ha seguido el método secuencial. Este método también maneja el caso donde no hay argumentos, devolviendo "". Puede ajustar fácilmente ese comportamiento insertando la
else
cláusula comentada .fuente
Para tcsh:
Estoy bastante seguro de que esta sería una solución portátil, a excepción de la asignación.
fuente
Encontré la respuesta de @ AgileZebra (más el comentario de @ starfry) la más útil, pero se establece
heads
en un escalar. Una matriz es probablemente más útil:Tenga en cuenta que esto es solo bash.
fuente
echo "${heads[-1]}"
imprime el último elemento enheads
. ¿O me estoy perdiendo algo?Una solución usando
eval
:fuente
!
entonceslast="${!#}"
usaría el mismo . enfoque (referencia indirecta en $ #) en un lugar más seguro, compacto, incorporado, de manera mucho sano y adecuadamente citado.=
.${!#}
, pero no en general: todavía se necesitan comillas si el contenido contiene espacios en blanco literales, comolast='two words'
. Solo$()
es seguro para espacios en blanco independientemente del contenido.Después de leer las respuestas anteriores, escribí un script de shell Q&D (debería funcionar en sh y bash) para ejecutar g ++ en PGM.cpp para producir una imagen ejecutable PGM. Se supone que el último argumento en la línea de comando es el nombre del archivo (.cpp es opcional) y todos los demás argumentos son opciones.
fuente
Lo siguiente establecerá el
LAST
último argumento sin cambiar el entorno actual:Si ya no se necesitan otros argumentos y se pueden cambiar, se puede simplificar a:
Por razones de portabilidad siguientes:
se puede reemplazar con:
Reemplazando también
$()
con backquotes obtenemos:fuente
Ahora solo necesito agregar algo de texto porque mi respuesta fue demasiado corta para publicar. Necesito agregar más texto para editar.
fuente
$argv
pero no$#argv
-$argv[(count $argv)]
funciona en pescado).Esto es parte de mi función de copia:
Para usar en scripts, haga esto:
Explicación (la mayoría anidada primero):
$(echo '$'"$#")
vuelve$[nr]
donde[nr]
está el número de parámetros. Por ejemplo, la cadena$123
(sin expandir).echo $123
devuelve el valor del parámetro 123, cuando se evalúa.eval
simplemente se expande$123
al valor del parámetro, p. ej.last_arg
. Esto se interpreta como una cadena y se devuelve.Trabaja con Bash a partir de mediados de 2015.
fuente
fuente
Pruebe el siguiente script para encontrar el último argumento
Salida:
Gracias.
fuente
Hay una manera mucho más concisa de hacer esto. Los argumentos de una secuencia de comandos bash se pueden incorporar a una matriz, lo que simplifica mucho el manejo de los elementos. El siguiente script siempre imprimirá el último argumento pasado a un script.
Salida de muestra
fuente
Usando la expansión de parámetros (eliminar el comienzo coincidente):
También es fácil obtener todo antes del último:
fuente
Para devolver el último argumento del comando utilizado más recientemente, use el parámetro especial:
En este caso, funcionará si se usa dentro del script antes de que se haya invocado otro comando.
fuente
Solo úsalo
!$
.fuente