Cuando no se citan, $*
y $@
son lo mismo. No debe usar ninguno de estos, ya que pueden romperse inesperadamente tan pronto como tenga argumentos que contengan espacios o comodines.
"$*"
se expande a una sola palabra "$1c$2c..."
. Por c
lo general, es un espacio, pero en realidad es el primer personaje de IFS
, por lo que puede ser cualquier cosa que elija.
El único buen uso que he encontrado es:
unir argumentos con coma (versión simple)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
unir argumentos con el delimitador especificado (mejor versión)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
se expande para separar palabras: "$1"
"$2"
...
Esto es casi siempre lo que quieres. Expande cada parámetro posicional a una palabra separada, lo que lo hace perfecto para tomar argumentos de línea de comando o función y luego pasarlos a otro comando o función. Y debido a que se expande usando comillas dobles, significa que las cosas no se rompen si, por ejemplo, "$1"
contiene un espacio o un asterisco ( *
).
Escribamos un script llamado svim
que se ejecute vim
con sudo
. Haremos tres versiones para ilustrar la diferencia.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Todos estarán bien para casos simples, por ejemplo, un solo nombre de archivo que no contenga espacios:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Pero solo $*
y "$@"
funciona correctamente si tienes múltiples argumentos.
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
Y solo "$*"
y "$@"
funciona correctamente si tiene argumentos que contienen espacios.
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Entonces solo "$@"
funcionará correctamente todo el tiempo.
typeset
es cómo hacer una variable local en ksh
( bash
y ash
usar local
en su lugar). Significa IFS
que se restaurará a su valor anterior cuando la función regrese. Esto es importante, porque los comandos que ejecute después podrían no funcionar correctamente si IFS
está configurado en algo no estándar.
$*
. Siempre lo consideré completamente inútil ... unirme con un delimitador es un buen caso de uso.Respuesta corta: uso
"$@"
(tenga en cuenta las comillas dobles). Las otras formas son muy raramente útiles."$@"
Es una sintaxis bastante extraña. Se reemplaza por todos los parámetros posicionales, como campos separados. Si no hay parámetros posicionales ($#
es 0), se"$@"
expande a nada (no es una cadena vacía, sino una lista con 0 elementos), si hay un parámetro posicional, entonces"$@"
es equivalente a"$1"
, si hay dos parámetros posicionales, entonces"$@"
es equivalente a"$1" "$2"
etc."$@"
le permite pasar los argumentos de un script o función a otro comando. Es muy útil para los contenedores que hacen cosas como establecer variables de entorno, preparar archivos de datos, etc. antes de llamar a un comando con los mismos argumentos y opciones con los que se llamó al contenedor.Por ejemplo, la siguiente función filtra la salida de
cvs -nq update
. Además del filtrado de salida y el estado de retorno (que es el degrep
más que el decvs
), invocarcvssm
algunos argumentos se comporta como llamarcvs -nq update
con estos argumentos."$@"
se expande a la lista de parámetros posicionales. En los shells que admiten matrices, hay una sintaxis similar para expandir a la lista de elementos de la matriz:"${array[@]}"
(las llaves son obligatorias excepto en zsh). Una vez más, las comillas dobles son algo engañosas: protegen contra la división de campos y la generación de patrones de los elementos de la matriz, pero cada elemento de la matriz termina en su propio campo.Algunas conchas antiguas tenían lo que podría decirse que era un error: cuando no había argumentos posicionales, se
"$@"
expandían a un solo campo que contenía una cadena vacía, en lugar de a ningún campo. Esto condujo a la solución${1+"$@"}
(que se hizo famosa a través de la documentación de Perl ). Solo se ven afectadas las versiones anteriores del shell Bourne real y la implementación de OSF1, ninguno de sus reemplazos modernos compatibles (ash, ksh, bash, ...) se ven afectados./bin/sh
no se ve afectado en ningún sistema que se lanzó en el siglo XXI que conozca (a menos que cuente la versión de mantenimiento Tru64, e incluso allí/usr/xpg4/bin/sh
es seguro, por lo que solo#!/bin/sh
se ven afectados los#!/usr/bin/env sh
guiones , no los guiones, siempre que su RUTA esté configurada para el cumplimiento POSIX) . En resumen, esta es una anécdota histórica de la que no debe preocuparse."$*"
siempre se expande a una palabra. Esta palabra contiene los parámetros posicionales, concatenados con un espacio intermedio. (Más generalmente, el separador es el primer carácter del valor de laIFS
variable. Si el valor deIFS
es la cadena vacía, el separador es la cadena vacía). Si no hay parámetros posicionales, entonces"$*"
es la cadena vacía, si hay dos parámetros posicionales yIFS
tiene su valor predeterminado, entonces"$*"
es equivalente a"$1 $2"
, etc.$@
y las$*
comillas externas son equivalentes. Se expanden a la lista de parámetros posicionales, como campos separados, como"$@"
; pero cada campo resultante se divide en campos separados que se tratan como patrones comodín de nombre de archivo, como es habitual con expansiones de variables sin comillas.Por ejemplo, si el directorio actual contiene tres archivos
bar
,baz
yfoo
, a continuación:fuente
"$@"
hecho se expandió a una lista que consta de la cadena vacía: unix.stackexchange.com/questions/68484/…Aquí hay un script simple para demostrar la diferencia entre
$*
y$@
:Salida:
En la sintaxis de matriz, no hay diferencia cuando se usa
$*
o$@
. Solo tiene sentido cuando los usa con comillas dobles"$*"
y"$@"
.fuente
IFS="^${IFS}"
embargo, ¿puedes explicar el uso de ?IFS
.IFS="^xxxxx"
lo haría? El${IFS}
sufijo final me hizo pensar que estaba haciendo algo más complicado, como recuperar de alguna manera automáticamente el IFS original al final (por ejemplo: el primer carácter se desplazó automáticamente o algo así).El código que proporcionó dará el mismo resultado. Para entenderlo mejor, intente esto:
La salida ahora debería ser diferente. Esto es lo que obtengo:
Esto funcionó para mí
bash
. Que yo sepa, ksh no debería diferir mucho. Esencialmente, las citas$*
tratarán todo como una palabra, y las citas$@
tratarán la lista como palabras separadas, como se puede ver en el ejemplo anterior.Como ejemplo de uso de la
IFS
variable con$*
, considere estoObtengo esto como resultado:
Además, acabo de confirmar que funciona igual
ksh
. Ambosbash
yksh
probados aquí estaban bajo OSX pero no puedo ver cómo eso importaría mucho.fuente
unset IFS
al final para restablecerlo al original, pero no funcionó para mí sin ningún problema y al hacerloecho $IFS
obtuve el resultado estándar que obtengo. La configuración de losIFS
corchetes introduce un nuevo alcance, por lo que, a menos que lo exporte, no afectará el exteriorIFS
.echo $IFS
no prueba nada, porque el shell ve el,
, pero luego divide palabras usandoIFS
! Tratarecho "$IFS"
.IFS
debería resolver eso.IFS
tenga un valor personalizado diferente antes de llamar a la función. Pero sí, la mayoría de las veces el IFS inquietante funcionará.La diferencia es importante al escribir scripts que deberían usar los parámetros posicionales de la manera correcta ...
Imagine la siguiente llamada:
Aquí solo hay 4 parámetros:
En mi caso,
myuseradd
es solo un contenedoruseradd
que acepta los mismos parámetros, pero agrega una cuota para el usuario:Observe la llamada a
useradd "$@"
, con$@
cita. Esto respetará los parámetros y los enviará tal como estánuseradd
. Si tuviera que poner entre comillas$@
(o usar$*
también sin comillas), useradd vería 5 parámetros, ya que el tercer parámetro que contenía un espacio se dividiría en dos:(ya la inversa, si se va a utilizar
"$*"
, useradd sería sólo ven un parámetro:-m -c Carlos Campderrós ccampderros
)En resumen, si necesita trabajar con parámetros que respeten parámetros de varias palabras, use
"$@"
.fuente
// hombre bash . es ksh, afair, comportamiento similar.
fuente
Hablando de diferencias entre
zsh
ybash
:Con comillas alrededor
$@
y$*
,zsh
y sebash
comportan igual, y supongo que el resultado es bastante estándar entre todos los shells:Sin comillas, los resultados son los mismos para
$*
y$@
, pero diferentes enbash
y enzsh
. En este casozsh
muestra un comportamiento extraño:(Zsh generalmente no divide datos textuales usando IFS, a menos que se solicite explícitamente, pero observe que aquí el argumento vacío falta inesperadamente en la lista).
fuente
$@
no es especial a este respecto: se$x
expande como máximo a una palabra, pero las variables vacías se expanden a nada (no es una palabra vacía). Pruebaprint -l a $foo b
confoo
vacío o indefinido.Una de las respuestas dice
$*
(que considero un "splat") rara vez es útil.Busco google con
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Dado que las URL a menudo se dividen con un
+
, pero mi teclado hace que seamás fácil de alcanzar que
+
,$*
+$IFS
siento que vale la pena.fuente