Acceder a la línea de comando bash args $ @ vs $ *

327

En muchas preguntas SO y tutoriales de bash, veo que puedo acceder a los argumentos de la línea de comandos en scripts de bash de dos maneras:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

Lo que resulta en:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

¿Cuál es la diferencia entre $*y $@?
¿Cuándo se debe usar el primero y cuándo se debe usar el segundo?

Oz123
fuente
eche un vistazo a esta respuesta: stackoverflow.com/a/842325/671366
codeling
análisis estático en IntelliJ trata echo "something $@"como un error
Alex Cohn

Respuestas:

437

La diferencia aparece cuando se citan los parámetros especiales. Permítanme ilustrar las diferencias:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

Otro ejemplo sobre la importancia de las citas: tenga en cuenta que hay 2 espacios entre "arg" y el número, pero si no puedo citar $ word:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

y en bash, "$@"es la lista "predeterminada" para iterar:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3
Glenn Jackman
fuente
65
+1 Siempre he pensado que este concepto se demostró mejor con un ejemplo simple, en el que falta el manual de bash por completo.
chepner
55
¿Hay un posible caso de uso, cuándo $*o "$*"puede ser requerido, y el propósito no puede ser cumplido por $@o "$@"?
anishsane
55
¿Qué versión es más adecuada para un script "envoltorio", donde los parámetros de los scripts deben convertirse en parámetros para un nuevo comando?
Segfault
77
@Segfault, en este caso, elija siempre "$@"con las comillas.
Glenn Jackman
2
Esta respuesta contiene ejemplos útiles, pero sería mejor si también explicara el mecanismo detrás de ellos. ¿ Por qué funciona así?
Lii
255

Una bonita y práctica tabla de resumen de Bash Hackers Wiki :

tabla $ * versus $ @

donde cen la tercera fila está el primer carácter de $IFS, el separador de campo interno, una variable de shell.

Si los argumentos se van a almacenar en una variable de script y se espera que contengan espacios, recomiendo encarecidamente emplear un "$*"truco con el separador de campo interno $IFSestablecido en tab .

Serge Stroobandt
fuente
42
... donde "c" es el primer personaje de $ IFS
glenn jackman
39
... y $IFSsignifica "Separador de campo interno".
Serge Stroobandt
Aquí hay un ejemplo , que incluye entradas citadas. ¡La entrada también importa!
Serge Stroobandt
Digamos que quiero crear un script de envoltura que no haga nada más que imitar la funcionalidad del comando envuelto. ¿Qué sintaxis debo usar para pasar los argumentos del script de envoltura al comando interno?
Marinos
44

PS

Se expande a los parámetros posicionales, comenzando desde uno. Cuando la expansión se produce entre comillas dobles, se expande a una sola palabra con el valor de cada parámetro separado por el primer carácter de la variable especial IFS. Es decir, "$ *" es equivalente a "$ 1c $ 2c ...", donde c es el primer carácter del valor de la variable IFS. Si IFS no está configurado, los parámetros están separados por espacios. Si IFS es nulo, los parámetros se unen sin separadores intermedios.

PS

Se expande a los parámetros posicionales, comenzando desde uno. Cuando la expansión se produce entre comillas dobles, cada parámetro se expande a una palabra separada. Es decir, "$ @" es equivalente a "$ 1" "$ 2" ... Si la expansión entre comillas dobles ocurre dentro de una palabra, la expansión del primer parámetro se une con la parte inicial de la palabra original, y la expansión del último parámetro se une con la última parte de la palabra original. Cuando no hay parámetros posicionales, "$ @" y $ @ se expanden a nada (es decir, se eliminan).

Fuente: Bash Man

Muffo
fuente
15

$ @ es igual a $ *, pero cada parámetro es una cadena entre comillas, es decir, los parámetros se pasan intactos, sin interpretación ni expansión. Esto significa, entre otras cosas, que cada parámetro en la lista de argumentos se ve como una palabra separada.

Por supuesto, se debe citar "$ @".

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

rkosegi
fuente
1

Este ejemplo permite resaltar la diferencia entre "at" y "asterix" mientras los usamos. Declaré dos matrices de "frutas" y "verduras"

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "${fruits[*]}"            
printf "Fruits:\t%s\n" "${fruits[@]}"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "${vegetables[*]}"    
printf "Vegetables:\t%s\n" "${vegetables[@]}"    

Vea el siguiente resultado del código anterior:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion
Stefansson
fuente
77
Científicamente hablando, los tomates son frutas.
Randy
1
¡Usted tiene derecho! "En botánica, un fruto es la estructura portadora de semillas en las plantas con flores (también conocidas como angiospermas) formadas a partir del ovario después de la floración". es.wikipedia.org/wiki/Fruit
stefansson
@Randy: científicamente hablando, todas las frutas son vegetales (es sinónimo de "planta").
Cris Luengo
¡Herejía de @CrisLuengo! :)
Randy