Propaga tutti gli argomenti in uno script di shell bash


853

Sto scrivendo uno script molto semplice che chiama un altro script e ho bisogno di propagare i parametri dal mio script corrente allo script che sto eseguendo.

Ad esempio, il mio nome di script è foo.she chiamabar.sh

foo.sh:

bar $1 $2 $3 $4

Come posso farlo senza specificare esplicitamente ogni parametro?



Risposte:


1408

Usa "$@"invece di semplice$@ se desideri che i tuoi parametri vengano passati allo stesso modo.

Osservare:

$ 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:

7
Questo funziona per puro passaggio di stringhe tra virgolette / escape: Osservare: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me È possibile avere script di shell in grado di vedere la vera riga di comando originale completa di virgolette o stringhe di escape?
Mark Edington,

3
Che dire del passaggio delle bandiere? Ad esempio, "./bar.sh --with-stuff"
Nathan Long,

4
Consiglio a chiunque voglia capire meglio l'argomento di Word Splitting, di leggere di più qui.
Rany Albeg Wein,

4
Ti suggerisco di mostrare cosa succede quando includi anche un 'arg with spaces'per i tre esempi. Sono stato sorpreso dai risultati; spero che tu possa spiegarli.
Michael Scheper,

2
@MichaelScheper, comunque, ./foo.sh "arg with spaces"e ./foo.sh 'arg with spaces'sono identici al 100%, quindi non vedo come il suggerimento di aggiungerlo agli esempi forniti sarebbe di alcun aiuto.
Charles Duffy,

475

Per bash e altre conchiglie tipo Bourne:

java com.myserver.Program "$@"

13
@Amir: No, non per csh . Per la sanità mentale di tutti: non scrivere csh . Ma penso che forse $argv:qfunzionerà in alcune varianti di csh.
Chris Johnsen,

2
Grazie! Se lo si desidera, questo passa lo stesso insieme di argomenti ricevuti dallo script, non un grande argomento. Le doppie virgolette sono necessarie. Funziona anche se con argomenti tra virgolette che includono spazi.
Andy Thomas,

24
Correlati: se lo script della shell agisce solo come un wrapper per eseguire java, prendere in considerazione la creazione dell'ultima riga exec java com.myserver.Program "$@"Questo fa sì che bash venga eseguito in java, piuttosto che attendere il completamento. Quindi, stai utilizzando uno slot di processo in meno. Inoltre, se il processo genitore (che ha eseguito il tuo script) lo sta guardando tramite il pid e si aspetta che sia il processo 'java', alcune cose insolite potrebbero rompersi se non esegui un exec; il exec fa sì che java erediti lo stesso pid.
Greggo,

7
@dragonxlwang: potresti usare una variabile array, se la tua shell le supporta (es. bash , zsh , altre, ma non semplice shell Bourne o POSIX): salva in arg con args=("$@")ed espandi ogni elemento come una "parola" separata della shell ( simile a "$@") con "${args[@]}".
Chris Johnsen,

1
Ci sono dei "trucchi" da tenere a mente quando si usa "$@", come fallirà se si è sfuggiti agli spazi in una discussione, ai caratteri null o ad altri caratteri speciali?
IQAndreas

97

Usa "$@"(funziona per tutti i compatibili POSIX ).

[...], bash presenta la variabile "$ @", che si espande a tutti i parametri della riga di comando separati da spazi.

Da Bash per esempio .


6
Quindi "$ @" non si limita a virgolette intorno a $ @, ma in effetti è una variabile incorporata diversa?
ben

5
@ben È una singola variabile ma richiede che le doppie virgolette attorno ad essa abbiano un valore utile distinto da (rotto) $*. Credo che ci sia una progressione storica qui; $*non ha funzionato come previsto, quindi è $@stato inventato per sostituirlo; ma le regole di quotazione sono quelle che sono, sono ancora richieste le doppie virgolette (o tornerà alla $*semantica interrotta ).
Tripleee,

7
"Quindi" $ @ "non si limita a virgolette intorno a $ @, ma in effetti una diversa variabile incorporata?" - A tutti gli effetti, sì: stackoverflow.com/a/28099707/162094
Stuart Berg

1
Se chiami uno script che contiene echo "$@"come ./script.sh a "b c" dallora ottieni semplicemente a b c dinvece a "b c" d, il che è molto diverso.
Isarandi,

7
@isarandi Mentre è vero che l'output non contiene più le virgolette, è quello che ti aspetteresti ... ma ti assicuro che all'interno dello script, echoriceve tre argomenti: "a" "b c" "d"(quindi la shell li unisce come parte dell'espansione della stringa). Ma se tu avessi usato, for i in "$@"; do echo $i; doneavresti ottenuto a⏎b c⏎d.
FeRD

64

Mi rendo conto che questo ha avuto una risposta soddisfacente, ma ecco un confronto tra "$ @" $ @ "$ *" e $ *

Contenuto dello script di 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 "================================="

Ora, esegui lo script di test con vari argomenti:

# ./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
=================================

1
buona dimostrazione concisa e contributo a questa risposta.
Merlin,

33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Ho solo pensato che questo potrebbe essere un po 'più utile quando provi a provare come args entra nella tua sceneggiatura


6
questo sicuramente non ha aiutato a rispondere alla sua domanda, ma questo è davvero utile. upvoted!
Dario Russo,

2
Si interrompe quando viene passato un parametro vuoto. Si dovrebbe verificare$#
Sebi

bravo args tester, d'accordo "", ''come argomento, anche se non ci fossero argomenti è silenzioso. Ho provato a risolvere questo problema, ma ho bisogno di un ciclo for e counter con $#. Ho appena aggiunto questo alla fine:echo "End of args or received quoted null"
Merlin,

8

Il mio SUN Unix ha molti limiti, anche "$ @" non è stato interpretato come desiderato. La mia soluzione alternativa è $ {@}. Per esempio,

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

A proposito, ho dovuto avere questo particolare script perché anche il mio Unix non supporta grep -r


Questa è una domanda Bash; stai usandoksh
tripleee il

6

Se includi $@ una stringa tra virgolette con altri caratteri, il comportamento è molto strano quando sono presenti più argomenti, all'interno delle virgolette viene incluso solo il primo argomento.

Esempio:

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

I rendimenti:

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

Ma assegnando prima a una variabile diversa:

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

I rendimenti:

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

3
Non nego che sia sconcertante, ma in realtà ha senso all'interno della semantica di "$@"in bash. Aiuta anche a illustrare la differenza chiave tra $@e $*e perché sono entrambi utili. Dalla bash(1)pagina dei parametri speciali della pagina man: " *- Quando l'espansione avviene tra virgolette doppie, si espande in una singola parola con il valore di ciascun parametro […] Cioè, "$*"è equivalente a "$1c$2c...", dove cè [ $IFS]." E infatti, usare $*invece che $@nel tuo primo esempio avrebbe generato un output identico alla seconda versione.
FeRD

2
Ora confrontalo con "$@". Sempre dalla pagina man: " @- Quando l'espansione avviene tra virgolette doppie, ogni parametro si espande in una parola separata. Cioè, "$@"equivale a "$1" "$2"... Se l'espansione tra virgolette si verifica all'interno di una parola, l'espansione del primo parametro viene unita con la parte iniziale della parola originale e l'espansione dell'ultimo parametro viene unita all'ultima parte della parola originale. " ... E in effetti, se il tuo codice fosse stato bash -c "true foo $@ bar baz", quindi eseguirlo come test.sh one twosarebbe net bash -c 'true foo one' 'two bar baz'.
FeRD

1
Grazie per la citazione dei documenti e informazioni su $*, mi sembra di dimenticare che esiste ..
ColinM

Eh. Sono l'opposto, $@stavo solo prendendo piede quando ho iniziato a scrivere script sulla shell, devo ancora ricordare a me stesso che è lì. Era comune vederlo "$*"usato negli script ... quindi l'autore si sarebbe reso conto che stava distruggendo tutti i loro argomenti insieme, quindi avrebbero provato ogni sorta di assurdità complesse con la divisione delle parole "$*"o [ri] assemblando una lista di argomenti in loop sopra shiftper tirarli giù uno per uno ... solo usando $@risolve. (Aiuta che bash usa lo stesso mnemonico anche per accedere ai membri dell'array: ${var[*]}per tutti loro come una parola, ${var[@]}per un elenco di parole.)
FeRD

Il vero problema qui è che stai usando bash -cin un modo che non ha assolutamente senso.
triplo il

4

A volte vuoi passare tutti i tuoi argomenti, ma preceduto da una bandiera (ad es. --flag)

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

Puoi farlo nel modo seguente:

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

nota: per evitare la divisione del campo in più, è necessario citare %se $@, per evitare di avere una sola stringa, non è possibile citare la sottostruttura di printf.


3

Funziona bene, tranne se hai spazi o caratteri di escape. Non trovo il modo di catturare argomenti in questo caso e inviarli a un ssh all'interno dello script.

Questo potrebbe essere utile ma è così brutto

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

3

Molte risposte qui raccomandano $@o $*con e senza virgolette, tuttavia nessuna sembra spiegare cosa fanno realmente e perché dovresti farlo. Vorrei quindi rubare questo eccellente riassunto da questa risposta :

inserisci qui la descrizione dell'immagine

Nota che le virgolette fanno la differenza e senza di esse entrambe hanno un comportamento identico.

Per il mio scopo, avevo bisogno di passare i parametri da uno script all'altro così com'è e per questo l'opzione migliore è:

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

Non notare virgolette e $@dovrebbe funzionare anche nella situazione sopra.


2

bar "$@" sarà equivalente a bar "$1" "$2" "$3" "$4"

Si noti che le virgolette sono importanti!

"$@", $@, "$*"O $*sarà ogni si comportano un po 'diversa per quanto riguarda la fuga e la concatenazione come descritto in questa risposta StackOverflow .

Un caso d'uso strettamente correlato sta passando tutti gli argomenti dati all'interno di un argomento come questo:

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

Uso una variante della risposta di @ kvantour per raggiungere questo obiettivo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.