¿Cuál es una mejor manera de implementar print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Si esto fuera, digamos, en #!/usr/bin/zsh
lugar de #!/bin/sh
saber qué hacer. Mi problema es encontrar una forma razonable de implementar esto #!/bin/sh
).
EDITAR: Lo anterior es solo un ejemplo tonto. Mi objetivo no es imprimir el último argumento, sino tener una manera de referirme al último argumento dentro de una función de shell.
EDIT2: Pido disculpas por una pregunta tan poco clara. Espero hacerlo bien esta vez.
Si esto fuera en /bin/zsh
lugar de /bin/sh
, podría escribir algo como esto
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
La expresión $argv[$#]
es un ejemplo de lo que describí en mi primer EDIT como una forma de referirme al último argumento dentro de una función de shell .
Por lo tanto, realmente debería haber escrito mi ejemplo original así:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... para dejar en claro que lo que busco es algo menos horrible de poner a la derecha de la tarea.
Sin embargo, tenga en cuenta que en todos los ejemplos, se accede al último argumento de forma no destructiva . IOW, el acceso al último argumento deja los argumentos posicionales en su conjunto sin verse afectados.
fuente
var=$( eval echo \${$#})
aeval var=\${$#}
- los dos son nada igual.shift
yset -- ...
basadas pueden ser destructivas a menos que se utilicen en funciones donde también son inofensivas.eval "var=\${$#}"
en comparación con,var=${arr[evaled index]}
excepto que$#
es un valor seguro garantizado. ¿Por qué copiar todo el conjunto y luego destruirlo cuando puedes indexarlo directamente?Respuestas:
... imprimirá la cadena
the last arg is
seguida de un <space> , el valor del último argumento y un <newline> final si hay al menos 1 argumento, o bien, para cero argumentos, no imprimirá nada en absoluto.Si lo hiciste:
... entonces asignaría el valor del último argumento a la variable de shell
$lastarg
si hay al menos 1 argumento, o no haría nada en absoluto. De cualquier manera, lo harías de forma segura, y debería ser portátil incluso para el caparazón de Olde Bourne, creo.Aquí hay otro que funcionaría de manera similar, aunque sí se requiere la copia de toda la matriz arg dos veces (y requiere una
printf
en$PATH
el shell Bourne) :fuente
${1+":"} return
inmediato, porque ¿quién quiere que haga algo o arriesgue los efectos secundarios de cualquier tipo si no se solicita nada? Las matemáticas son bonitas para lo mismo: si puede estar seguro de que puede expandir un número entero positivo, puede hacerloeval
todo el día.$OPTIND
es genial por esoAquí hay una forma simplista:
(actualizado según el punto de @ cuonglm de que el original fallaba cuando no se pasaba ningún argumento; esto ahora hace eco de una línea en blanco: cambie ese comportamiento en la
else
cláusula si lo desea)fuente
echo "$# - 1" | bc
Dado el ejemplo de la publicación de apertura (argumentos posicionales sin espacios):
Por defecto
IFS=' \t\n'
, ¿qué tal:Para una expansión más segura de
"$*"
, configure IFS (por @ StéphaneChazelas):Pero lo anterior fallará si sus argumentos posicionales pueden contener espacios. En ese caso, use esto en su lugar:
Tenga en cuenta que estas técnicas evitan el uso
eval
y no tienen efectos secundarios.Probado en shellcheck.net
fuente
$IFS
es un espacio.IFS=' '
aquí solo para dejar en claro que se está utilizando para la expansión de"$*"
.Aunque esta pregunta tiene poco más de 2 años, pensé en compartir una opción más compacta.
Vamos a correrlo
Expansión de parámetros de shell Bash .
Editar
Aún más conciso:
echo "${@: -1}"
(Cuidado con el espacio)
Fuente
Probado en macOS 10.12.6 pero también debería devolver el último argumento sobre la mayoría de los sabores * nix disponibles ...
Duele mucho menos
¯\_(ツ)_/¯
fuente
echo "${*: -1}"
loshellcheck
que no se quejará.${array:n:m:}
es una extensión. (la pregunta mencionó explícitamente/bin/sh
)POSIXY:
(Este enfoque también funciona en el viejo shell Bourne)
Con otras herramientas estándar:
(Esto no funcionará con el viejo
awk
, que no teníaARGV
)fuente
awk
también podría ser el viejo awk que no teníaARGV
.awk
enfoque, o use otrofor
ciclo simple como lo hizo otro, o páselos a una función.Esto debería funcionar con cualquier shell compatible con POSIX y también con el shell Solaris Bourne pre POSIX heredado:
y aquí hay una función basada en el mismo enfoque:
PD: no me digas que olvidé las llaves alrededor del cuerpo de la función ;-)
fuente
in ...
de unfor
bucle como las llaves sin rizos alrededor de unaif
declaración utilizada como cuerpo de la función. Verfor_clause
ycompound_command
en pubs.opengroup.org/onlinepubs/9699919799 .De "Unix - Preguntas frecuentes"
(1)
Si el número de argumentos puede ser cero, entonces
$0
se asignará el argumento cero (generalmente el nombre del script)$last
. Esa es la razón del if.(2)
(3)
Para evitar imprimir una línea vacía cuando no hay argumentos, reemplace
echo "$last"
for:Se evita un recuento de argumento cero
if [ $# -gt 0 ]
.Esta no es una copia exacta de lo que está en el enlace en la página, se agregaron algunas mejoras.
fuente
Esto debería funcionar en todos los sistemas con
perl
instalado (por lo que la mayoría de UNICES):El truco es usar
printf
para agregar un\0
argumento después de cada argumento de shell y luegoperl
el-0
interruptor que establece su separador de registros en NULL. Luego iteramos sobre la entrada, eliminamos y guardamos\0
cada línea terminada en NULL como$l
. ElEND
bloque (eso es lo que}{
es) se ejecutará después de que se haya leído toda la entrada, por lo que imprimirá la última "línea": el último argumento de shell.fuente
sed
,awk
operl
entonces podría ser una valiosa información de todos modos. aún puede hacer lo mismo con lassed -z
versiones actualizadas de GNU.Aquí hay una versión con recursión. Sin embargo, no tengo idea de cuán compatible con POSIX ...
fuente
Un concepto simple, sin aritmética, sin bucle, sin evaluación , solo funciones.
Recuerde que el shell Bourne no tenía aritmética (necesaria externa
expr
). Si desea obtener una aritmética libre,eval
libre elección, esta es una opción. Necesitar funciones significa SVR3 o superior (sin sobrescritura de parámetros).Mire abajo para una versión más robusta con printf.
Esta estructura a la llamada
printlast
se fija , los argumentos se deben establecer en la lista de argumentos de la cáscara$1
,$2
, etc. (la pila argumento) y la llamada hecho como dado.Si la lista de argumentos necesita ser cambiada, solo configúrelos:
O cree una función (
getlast
) más fácil de usar que pueda permitir argumentos genéricos (pero no tan rápido, los argumentos se pasan dos veces).Tenga en cuenta que los argumentos (de
getlast
, o todos incluidos en$@
for lastlast) podrían tener espacios, líneas nuevas, etc. Pero no NUL.Mejor
Esta versión no se imprime
0
cuando la lista de argumentos está vacía, y utiliza el printf más robusto (recurrir aecho
si externoprintf
no está disponible para shells antiguos).Usando EVAL.
Si el shell Bourne es aún más antiguo y no hay funciones, o si por alguna razón usar eval es la única opción:
Para imprimir el valor:
Para establecer el valor en una variable:
Si eso debe hacerse en una función, los argumentos deben copiarse en la función:
fuente
eval
?for loop
owhile loop
?echo "Hello"
tiene bucles ya que la CPU tiene que leer las ubicaciones de memoria en un bucle. Nuevamente: mi código no tiene bucles agregados.for
,while
ountil
bucles. ¿Ves algunofor
while
o ununtil
bucle escrito en el código? ¿No ?, entonces solo acepta el hecho, abrázalo y aprende.Este comentario sugirió usar el muy elegante:
fuente