Propager tous les arguments dans un script shell bash

854

J'écris un script très simple qui appelle un autre script, et je dois propager les paramètres de mon script actuel au script que j'exécute.

Par exemple, mon nom de script est foo.shet appellebar.sh

foo.sh:

bar $1 $2 $3 $4

Comment puis-je faire cela sans spécifier explicitement chaque paramètre?

Fragsworth
la source

Réponses:

1409

Utilisez "$@"plutôt que plain $@si vous souhaitez que vos paramètres soient transmis de la même manière.

Observer:

$ 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
la source
7
Cela fonctionne pour le passage pur des chaînes entre guillemets / échappées: Observez: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me Est-il possible d'avoir un script shell qui peut voir la vraie ligne de commande d'origine avec des guillemets ou des chaînes d'échappement?
Mark Edington
3
Et passer des drapeaux? Par exemple, './bar.sh --with-stuff'
Nathan Long
4
Je suggère à quiconque souhaite mieux comprendre le sujet de la division des mots d'en lire plus ici.
Rany Albeg Wein
4
Je vous suggère également de montrer ce qui se passe lorsque vous incluez un 'arg with spaces'pour les trois exemples. J'ai été surpris par les résultats; j'espère que vous pourrez les expliquer.
Michael Scheper
2
@MichaelScheper, de toute façon, ./foo.sh "arg with spaces"et ./foo.sh 'arg with spaces'sont 100% identiques, donc je ne vois pas comment la suggestion de l'ajouter aux exemples donnés serait d'une quelconque utilité.
Charles Duffy
475

Pour bash et autres obus de type Bourne:

java com.myserver.Program "$@"
Chris Johnsen
la source
13
@Amir: Non, pas pour csh . Pour la raison de tout le monde: ne pas script csh . Mais je pense que peut $argv:q- être fonctionnera dans certaines variantes de csh.
Chris Johnsen
2
Merci! Comme souhaité, cela transmet le même ensemble d'arguments reçus par le script - pas un seul gros argument. Les guillemets doubles sont nécessaires. Fonctionne même avec des arguments entre guillemets qui incluent des espaces.
Andy Thomas
24
En relation: si votre script shell agit uniquement comme un wrapper pour exécuter java, envisagez de créer la dernière ligne exec java com.myserver.Program "$@"Cela provoque l'exécution de bash en java, plutôt que d'attendre qu'il se termine. Vous utilisez donc un emplacement de processus en moins. De plus, si le processus parent (qui a exécuté votre script) le regarde via le pid et s'attend à ce que ce soit le processus 'java', certaines choses inhabituelles pourraient se casser si vous ne faites pas d'exec; l'exec fait java hériter du même pid.
greggo
7
@dragonxlwang: Vous pouvez utiliser une variable de tableau, si votre shell les prend en charge (par exemple bash , zsh , autres, mais pas le Bourne- ou le POSIX-shell ordinaire): enregistrez dans args avec args=("$@")et développez chaque élément en tant que «mot» shell séparé ( semblable à "$@") avec "${args[@]}".
Chris Johnsen
1
Y a-t-il des "accrochages" à garder à l'esprit lors de l'utilisation "$@", comme cela échouera si vous avez échappé des espaces dans un argument, ou des caractères nuls, ou d'autres caractères spéciaux?
IQAndreas
97

Utilisation "$@"(fonctionne pour tous les compatibles POSIX ).

[...], bash comporte la variable "$ @", qui s'étend à tous les paramètres de ligne de commande séparés par des espaces.

De Bash par exemple .

miku
la source
6
"$ @" N'est-il pas seulement des guillemets autour de $ @, mais en fait une variable intégrée différente?
ben
5
@ben Il s'agit d'une variable unique mais elle nécessite que les guillemets doubles qui l'entourent aient une valeur utile distincte du (cassé) $*. Je crois qu'il y a une progression historique ici; $*ne fonctionnait pas comme prévu, a donc $@été inventé pour le remplacer; mais les règles de citation étant ce qu'elles sont, les doubles guillemets qui l'entourent sont toujours requis (ou cela reviendra à la $*sémantique brisée ).
tripleee du
7
"Est-ce que" $ @ "n'est pas seulement des guillemets autour de $ @ mais en fait une variable intégrée différente?" - À toutes fins utiles, oui: stackoverflow.com/a/28099707/162094
Stuart Berg
1
Si vous appelez un script contenant echo "$@"as, ./script.sh a "b c" dvous obtenez simplement a b c dau lieu de a "b c" d, ce qui est très différent.
isarandi
7
@isarandi S'il est vrai que la sortie ne contient plus les guillemets, c'est ce à quoi vous vous attendez ... mais soyez assuré que dans le script, echoreçoit trois arguments: "a" "b c" "d"(puis le shell les joint ensemble dans le cadre de son expansion de chaîne). Mais si vous aviez utilisé, for i in "$@"; do echo $i; donevous auriez obtenu a⏎b c⏎d.
FeRD
64

Je sais que cela a été bien répondu, mais voici une comparaison entre "$ @" $ @ "$ *" et $ *

Contenu du script de test:

# 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 "================================="

Maintenant, exécutez le script de test avec divers arguments:

# ./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
la source
1
bonne démonstration succincte et contribution à cette réponse.
Merlin
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Je pensais que cela pourrait être un peu plus utile lorsque vous essayez de tester comment les arguments entrent dans votre script

Gregory Patmore
la source
6
cela n'a certainement pas aidé à répondre à sa question mais c'est effectivement utile. voté!
Dario Russo
2
Il se casse lorsqu'un paramètre vide est passé. Vous devriez vérifier$#
Sebi
bon testeur d'arguments , d'accord "", ''comme argument, même s'il n'y a pas d'arguments, il est silencieux. J'ai essayé de résoudre ce problème, mais j'ai besoin d'une boucle for et d'un compteur avec $#. Je viens d'ajouter ceci à la fin:echo "End of args or received quoted null"
Merlin
8

Mon SUN Unix a beaucoup de limitations, même "$ @" n'a pas été interprété comme souhaité. Ma solution de contournement est $ {@}. Par exemple,

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

Soit dit en passant, je devais avoir ce script particulier parce que mon Unix ne supporte pas non plus grep -r

Hampden123
la source
Ceci est une question Bash; vous utilisezksh
tripleee
6

Si vous incluez $@ dans une chaîne entre guillemets d'autres caractères, le comportement est très étrange lorsqu'il y a plusieurs arguments, seul le premier argument est inclus à l'intérieur des guillemets.

Exemple:

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

Rendements:

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

Mais affectons d'abord à une variable différente:

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

Rendements:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
la source
3
Je ne nierai pas que c'est déconcertant, mais cela a du sens dans la sémantique de "$@"in bash. Cela permet également d'illustrer la principale différence entre $@et $*et pourquoi ils sont tous les deux utiles. À partir de la bash(1)page de manuel de la section Paramètres spéciaux: " *- Lorsque l'expansion se produit entre guillemets, elle se développe en un seul mot avec la valeur de chaque paramètre […] Autrement dit, "$*"est équivalent à "$1c$2c...", où cest [ $IFS]." Et en effet, utiliser à la $*place de $@dans votre premier exemple aurait généré une sortie identique à la deuxième version.
FeRD
2
Maintenant, comparez cela à "$@". Toujours à partir de la page de manuel: " @- Lorsque l'expansion se produit entre guillemets doubles, chaque paramètre se développe en un mot distinct. C'est-à-dire, "$@"est équivalent à "$1" "$2"... Si l'expansion entre guillemets se produit dans un mot, l'expansion du premier paramètre est jointe avec la partie de début du mot d'origine, et l'expansion du dernier paramètre est jointe à la dernière partie du mot d'origine. " ... Et en effet, si votre code avait été bash -c "true foo $@ bar baz", alors exécutez-le comme le test.sh one twoferait net bash -c 'true foo one' 'two bar baz'.
FeRD
1
Merci pour la citation et les informations sur les documents $*, je semble oublier qu'elle existe ..
ColinM
Il h. Je suis l'opposé, $@je commençais à gagner du terrain lorsque j'ai commencé à écrire des scripts shell, je dois encore me rappeler qu'il est là. Il était courant de les voir "$*"utilisés dans des scripts ... alors l'auteur se rendrait compte qu'il brisait tous leurs arguments ensemble, alors ils essayaient toutes sortes de bêtises complexes avec le fractionnement de mots "$*", ou [ré] assemblaient une liste d'arguments en boucle plus shiftpour les tirer un par un ... juste en utilisant $@résout le problème. (Aide que bash utilise également le même mnémonique pour accéder aux membres du tableau: ${var[*]}pour tous en tant que mot, ${var[@]}pour une liste de mots.)
FeRD
Le vrai problème ici est que vous utilisez bash -cd'une manière qui n'a absolument aucun sens.
tripleee
4

Parfois, vous voulez passer tous vos arguments, mais précédé d'un indicateur (par exemple --flag)

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

Vous pouvez le faire de la manière suivante:

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

Remarque: pour éviter la division de champs supplémentaires, vous devez citer %set $@, et pour éviter d'avoir une seule chaîne, vous ne pouvez pas citer le sous-shell de printf.

kvantour
la source
3

Fonctionne bien, sauf si vous avez des espaces ou des caractères d'échappement. Je ne trouve pas le moyen de capturer les arguments dans ce cas et de les envoyer à un ssh à l'intérieur du script.

Cela pourrait être utile mais si moche

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

Beaucoup de réponses ici recommandent $@ou $*avec et sans guillemets, mais aucune ne semble expliquer ce que cela fait vraiment et pourquoi vous devriez le faire. Alors laissez-moi voler cet excellent résumé de cette réponse :

entrez la description de l'image ici

Notez que les guillemets font toute la différence et sans eux, les deux ont un comportement identique.

Dans mon but, j'avais besoin de passer les paramètres d'un script à un autre tels quels et pour cela la meilleure option est:

# 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 $*

Ne remarquez aucun devis et $@devrait également fonctionner dans la situation ci-dessus.

Shital Shah
la source
2

bar "$@" sera équivalent à bar "$1" "$2" "$3" "$4"

Notez que les guillemets sont importants!

"$@", $@, "$*"Ou $*seront chacun se comportent légèrement différemment en ce qui concerne s'échapper et concaténation comme décrit dans cette réponse stackoverflow .

Un cas d'utilisation étroitement lié passe tous les arguments donnés à l'intérieur d'un argument comme celui-ci:

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

J'utilise une variante de la réponse de @ kvantour pour y parvenir:

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

Davidiusdadi
la source