Propague todos los argumentos en un script de shell bash

854

Estoy escribiendo un script muy simple que llama a otro script, y necesito propagar los parámetros de mi script actual al script que estoy ejecutando.

Por ejemplo, mi nombre de script es foo.shy llamabar.sh

foo.sh:

bar $1 $2 $3 $4

¿Cómo puedo hacer esto sin especificar explícitamente cada parámetro?

Fragsworth
fuente

Respuestas:

1409

Use en "$@"lugar de simple $@si realmente desea que sus parámetros pasen lo mismo.

Observar:

$ cat foo.sh
#!/bin/bash
baz.sh $@

$ cat bar.sh
#!/bin/bash
baz.sh "$@"

$ cat baz.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4

$ ./foo.sh first second
Received: first
Received: second
Received:
Received:

$ ./foo.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:

$ ./bar.sh first second
Received: first
Received: second
Received:
Received:

$ ./bar.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
Sdaz MacSkibbons
fuente
77
Esto funciona para el paso puro de cadenas citadas / escapadas: Observe: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directorio bar me ¿Es posible tener un script de shell que pueda ver la verdadera línea de comando original completa con comillas o cadenas escapadas?
Mark Edington
3
¿Qué hay de pasar banderas? Por ejemplo, './bar.sh --with-stuff'
Nathan Long
44
Sugiero a cualquiera que quiera comprender mejor el tema de la división de palabras, que lea más sobre esto aquí.
Rany Albeg Wein
44
Le sugiero que muestre lo que sucede al incluir un 'arg with spaces'para los tres ejemplos, también. Me sorprendieron los resultados; espero que puedas explicarlos.
Michael Scheper
2
@MichaelScheper, de todos modos, ./foo.sh "arg with spaces"y ./foo.sh 'arg with spaces'son 100% idénticos, por lo que no veo cómo la sugerencia de agregarlo a los ejemplos dados sería de alguna ayuda.
Charles Duffy
475

Para bash y otras conchas tipo Bourne:

java com.myserver.Program "$@"
Chris Johnsen
fuente
13
@Amir: No, no para csh . Para la cordura de todos: no escriba csh . Pero creo que tal $argv:qvez funcione en algunas variantes de csh.
Chris Johnsen
2
¡Gracias! Como se desea, esto pasa el mismo conjunto de argumentos recibidos por el script, no un gran argumento. Las comillas dobles son necesarias. Funciona incluso con argumentos entre comillas que incluyen espacios.
Andy Thomas
24
Relacionado: si su script de shell solo está actuando como un contenedor para ejecutar java, considere hacer la última línea exec java com.myserver.Program "$@"Esto hace que bash se ejecute en java, en lugar de esperar a que se complete. Entonces, está utilizando una ranura de proceso menos. Además, si el proceso padre (que ejecutó su script) lo está viendo a través del pid, y espera que sea el proceso 'java', algunas cosas inusuales podrían romperse si no hace un ejecutivo; El ejecutivo hace que Java herede el mismo pid.
greggo
77
@dragonxlwang: podría usar una variable de matriz, si su shell los admite (por ejemplo , bash , zsh , otros, pero no Bourne- o POSIX-shell): guarde en argumentos con args=("$@")y expanda cada elemento como una "palabra" de shell separada ( similar a "$@") con "${args[@]}".
Chris Johnsen
1
¿Hay alguna "trampa" a tener en cuenta cuando se usa "$@", como fallará si ha escapado de espacios en una discusión, caracteres nulos u otros caracteres especiales?
IQAndreas
97

Uso "$@"(funciona para todos los POSIX compatibles).

[...], bash presenta la variable "$ @", que se expande a todos los parámetros de línea de comandos separados por espacios.

De Bash por ejemplo .

miku
fuente
66
Entonces, ¿"$ @" no solo cotiza alrededor de $ @ sino que, en efecto, es una variable integrada diferente?
ben
55
@ben Es una variable única pero requiere que las comillas dobles a su alrededor tengan un valor útil distinto del (roto) $*. Creo que hay una progresión histórica aquí; $*no funcionó como se diseñó, por lo que $@se inventó para reemplazarlo; pero las reglas de comillas son lo que son, las comillas dobles a su alrededor todavía se requieren (o revertirá a la $*semántica rota ).
tripleee
77
"Entonces, ¿" $ @ "no solo cotiza alrededor de $ @ sino que, en efecto, es una variable integrada diferente?" - Para todos los efectos, sí: stackoverflow.com/a/28099707/162094
Stuart Berg el
1
Si llama a un script que contiene echo "$@"como ./script.sh a "b c" d, solo obtiene en a b c dlugar de a "b c" d, que es muy diferente.
isarandi
77
@isarandi Si bien es cierto que la salida ya no contiene las comillas, eso es lo que cabría esperar ... pero tenga la seguridad de que dentro del script, echorecibe tres argumentos: "a" "b c" "d"(luego el shell los une como parte de su expansión de cadena). Pero si lo hubieras usado, for i in "$@"; do echo $i; donelo hubieras conseguido a⏎b c⏎d.
FeRD
64

Me doy cuenta de que esto ha sido bien respondido, pero aquí hay una comparación entre "$ @" $ @ "$ *" y $ *

Contenido del guión de prueba:

# cat ./test.sh
#!/usr/bin/env bash
echo "================================="

echo "Quoted DOLLAR-AT"
for ARG in "$@"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-AT"
for ARG in $@; do
    echo $ARG
done

echo "================================="

echo "Quoted DOLLAR-STAR"
for ARG in "$*"; do
    echo $ARG
done

echo "================================="

echo "NOT Quoted DOLLAR-STAR"
for ARG in $*; do
    echo $ARG
done

echo "================================="

Ahora, ejecute el script de prueba con varios argumentos:

# ./test.sh  "arg with space one" "arg2" arg3
=================================
Quoted DOLLAR-AT
arg with space one
arg2
arg3
=================================
NOT Quoted DOLLAR-AT
arg
with
space
one
arg2
arg3
=================================
Quoted DOLLAR-STAR
arg with space one arg2 arg3
=================================
NOT Quoted DOLLAR-STAR
arg
with
space
one
arg2
arg3
=================================
JDS
fuente
1
Buena demostración sucinta y contribución a esta respuesta.
Merlín
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Solo pensé que esto podría ser un poco más útil al intentar probar cómo los argumentos entran en tu script

Gregory Patmore
fuente
66
Esto definitivamente no ayudó a responder su pregunta, pero esto es realmente útil. ¡Votado!
Dario Russo
2
Se rompe cuando se pasa un parámetro vacío. Usted debe verificar$#
Sebi
buen probador de args, de acuerdo "", ''como argumento, también si no hubo args, es silencioso. Intenté arreglar esto, pero necesito un bucle for y un contador con $#. Acabo de agregar esto al final:echo "End of args or received quoted null"
Merlín
8

Mi SUN Unix tiene muchas limitaciones, incluso "$ @" no se interpretó como se deseaba. Mi solución alternativa es $ {@}. Por ejemplo,

#!/bin/ksh
find ./ -type f | xargs grep "${@}"

Por cierto, tenía que tener este script en particular porque mi Unix tampoco es compatible con grep -r

Hampden123
fuente
Esta es una pregunta Bash; estás usandoksh
tripleee
6

Si incluye $@una cadena entre comillas con otros caracteres, el comportamiento es muy extraño cuando hay varios argumentos, solo el primer argumento se incluye dentro de las comillas.

Ejemplo:

#!/bin/bash
set -x
bash -c "true foo $@"

Rendimientos:

$ bash test.sh bar baz
+ bash -c 'true foo bar' baz

Pero asignando a una variable diferente primero:

#!/bin/bash
set -x
args="$@"
bash -c "true foo $args"

Rendimientos:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
fuente
3
No negaré que eso es desconcertante, pero en realidad tiene sentido dentro de la semántica de "$@"in bash. También ayuda a ilustrar la diferencia clave entre $@y $*, y por qué ambos son útiles. Desde la bash(1)sección de parámetros especiales de la página de manual: " *- Cuando la expansión se produce entre comillas dobles, se expande a una sola palabra con el valor de cada parámetro [...] Es decir, "$*"es equivalente a "$1c$2c...", donde cestá [ $IFS]". Y, de hecho, usar en $*lugar de $@en su primer ejemplo habría generado una salida idéntica a la segunda versión.
FeRD
2
Ahora, compara eso con "$@". De nuevo desde la página del manual: " @- 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 se produce 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 ". ... Y, de hecho, si tu código hubiera estado bash -c "true foo $@ bar baz", entonces ejecútalo como lo test.sh one twoharía net bash -c 'true foo one' 'two bar baz'.
FeRD
1
Gracias por la cita y la información sobre los documentos $*, parece olvidar que existe ...
ColinM
Je Soy todo lo contrario, $@estaba ganando terreno cuando comencé a usar scripts de shell, todavía tengo que recordarme a mí mismo que está allí. Era común ver que se "$*"usaba en los scripts ... entonces el autor se daría cuenta de que estaba rompiendo todos sus argumentos juntos, por lo que probarían todo tipo de tonterías complejas con división de palabras "$*", o [re] ensamblar una lista arg por bucle más shiftque tirar de ellos uno por uno ... simplemente usando $@lo resuelve. (Ayuda a que bash use la misma mnemónica para acceder a los miembros de la matriz, también: ${var[*]}para todos ellos como una palabra, ${var[@]}para una lista de palabras.)
FeRD
El verdadero problema aquí es que está utilizando bash -cde una manera que no tiene ningún sentido.
tripleee
4

A veces desea pasar todos sus argumentos, pero precedido por una bandera (por ejemplo --flag)

$ bar --flag "$1" --flag "$2" --flag "$3"

Puede hacer esto de la siguiente manera:

$ bar $(printf -- ' --flag "%s"' "$@")

nota: para evitar una división de campo adicional, debe citar %sy $@, y para evitar tener una sola cadena, no puede citar la subshell de printf.

kvantour
fuente
3

Funciona bien, excepto si tiene espacios o caracteres escapados. No encuentro la forma de capturar argumentos en este caso y enviarlos a un ssh dentro del script.

Esto podría ser útil pero es tan feo

_command_opts=$( echo "$@" | awk -F\- 'BEGIN { OFS=" -" } { for (i=2;i<=NF;i++) { gsub(/^[a-z] /,"&@",$i) ; gsub(/ $/,"",$i );gsub (/$/,"@",$i) }; print $0 }' | tr '@' \' )
usuario6650302
fuente
3

Muchas respuestas aquí recomiendan $@o $*con o sin citas, sin embargo, ninguna parece explicar lo que realmente hacen y por qué debería hacerlo de esa manera. Así que déjame robar este excelente resumen de esta respuesta :

ingrese la descripción de la imagen aquí

Tenga en cuenta que las citas marcan la diferencia y sin ellas ambas tienen un comportamiento idéntico.

Para mi propósito, necesitaba pasar los parámetros de un script a otro tal cual y para eso la mejor opción es:

# file: parent.sh
# we have some params passed to parent.sh 
# which we will like to pass on to child.sh as-is

./child.sh $*

Observe que no hay comillas y $@debería funcionar tan bien en la situación anterior.

Shital Shah
fuente
2

bar "$@" será equivalente a bar "$1" "$2" "$3" "$4"

Tenga en cuenta que las comillas son importantes!

"$@", $@, "$*"O $*se comportan cada uno un poco diferente con respecto a escapar y la concatenación como se describe en esta respuesta stackoverflow .

Un caso de uso estrechamente relacionado es pasar todos los argumentos dados dentro de un argumento como este:

bash -c "bar \"$1\" \"$2\" \"$3\" \"$4\"".

Utilizo una variación de la respuesta de @ kvantour para lograr esto:

bash -c "bar $(printf -- '"%s" ' "$@")"

Davidiusdadi
fuente