Propagar todos os argumentos em um script shell bash

853

Estou escrevendo um script muito simples que chama outro script e preciso propagar os parâmetros do meu script atual para o script que estou executando.

Por exemplo, meu nome de script é foo.she chamabar.sh

foo.sh:

bar $1 $2 $3 $4

Como posso fazer isso sem especificar explicitamente cada parâmetro?

Fragsworth
fonte

Respostas:

1408

Use em "$@"vez de simples $@se você realmente deseja que seus parâmetros sejam passados ​​da mesma forma.

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
fonte
7
Isso funciona para a passagem pura de seqüências de caracteres citadas / com escape: Observe: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me É possível ter um shell script que possa ver a verdadeira linha de comando original completa com aspas ou seqüências de caracteres de escape?
Mark Edington
3
Que tal passar bandeiras? Por exemplo, './bar.sh --with-stuff'
Nathan Long
4
Sugiro que quem quer entender melhor o assunto da Divisão de Palavras, leia mais sobre isso aqui.
Rany Albeg Wein
4
Sugiro que você mostre o que acontece ao incluir um 'arg with spaces'para os três exemplos também. Fiquei surpreso com os resultados; espero que você possa explicá-los.
Michael Scheper
2
@MichaelScheper, de qualquer forma, ./foo.sh "arg with spaces"e ./foo.sh 'arg with spaces'são 100% idênticos, então não vejo como a sugestão de adicioná-lo aos exemplos dados seria de alguma ajuda.
Charles Duffy
475

Para bash e outras conchas do tipo Bourne:

java com.myserver.Program "$@"
Chris Johnsen
fonte
13
@Amir: Não, não para csh . Para a sanidade de todos: não escreva csh . Mas acho que talvez $argv:qfuncione em algumas variantes do csh.
Chris Johnsen
2
Obrigado! Como desejado, isso passa o mesmo conjunto de argumentos recebidos pelo script - não um grande argumento. As aspas duplas são necessárias. Funciona mesmo que com argumentos citados que incluam espaços.
Andy Thomas
24
Relacionado: se o seu script shell estiver apenas atuando como um wrapper para executar o java, considere criar a última linha exec java com.myserver.Program "$@"Isso faz com que o bash seja executado em java, em vez de esperar que ele seja concluído. Então, você está usando um slot de processo a menos. Além disso, se o processo pai (que executou o seu script) o estiver assistindo via pid, e esperando que seja o processo 'java', algumas coisas incomuns poderão ser quebradas se você não executar um exec; o exec faz com que o java herde o mesmo pid.
Greggo
7
@dragonxlwang: Você pode usar uma variável de matriz, se o seu shell apoia-los (por exemplo, o bash , zsh , outros, Bourne-, mas não simples ou POSIX-shell): salvar para args com args=("$@")e expandir cada elemento como uma “palavra” separado shell ( semelhante a "$@") com "${args[@]}".
22415 Chris Chrissen
1
Há alguma "dica" a ser lembrada ao usar "$@", como falhará se você tiver escapado de espaços em um argumento, caracteres nulos ou outros caracteres especiais?
IQAndreas 03/04
97

Use "$@"(funciona para todos os compatíveis com POSIX ).

[...], o bash apresenta a variável "$ @", que se expande para todos os parâmetros da linha de comando separados por espaços.

Do Bash por exemplo .

miku
fonte
6
Então "$ @" não é apenas aspas em torno de $ @, mas na verdade uma variável integrada diferente?
ben
5
@ben É uma variável única, mas requer que as aspas duplas ao seu redor tenham um valor útil distinto do (quebrado) $*. Eu acredito que há progressão histórica aqui; $*não funcionou como planejado, por isso $@foi inventado para substituí-lo; mas as regras de citação são o que são, as aspas duplas ainda são necessárias (ou serão revertidas para a $*semântica quebrada ).
Tripleee
7
"Então," $ @ "não é apenas aspas em torno de $ @, mas na verdade uma variável integrada diferente?" - Para todos os efeitos, sim: stackoverflow.com/a/28099707/162094
Stuart Berg
1
Se você chamar um script que contenha echo "$@"as ./script.sh a "b c" d, é só pegar em a b c dvez de a "b c" d, o que é muito diferente.
Isarandi 16/0518
7
@isarandi Embora seja verdade que a saída não contenha mais aspas, é isso que você esperaria ... mas tenha certeza de que, dentro do script, echorecebe três argumentos: "a" "b c" "d"(então o shell os une como parte de sua expansão de string). Mas se você tivesse usado for i in "$@"; do echo $i; doneVocê teria conseguido a⏎b c⏎d.
FeRD
64

Sei que isso foi bem respondido, mas aqui está uma comparação entre "$ @" $ @ "$ *" e $ *

Conteúdo do script de teste:

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

Agora, execute o script de teste com vários 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
fonte
1
boa demonstração sucinta e contribuição para esta resposta.
Merlin
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Apenas pensei que isso poderia ser um pouco mais útil ao tentar testar como os argumentos de args entram em seu script

Gregory Patmore
fonte
6
isso definitivamente não ajudou a responder sua pergunta, mas é realmente útil. votado!
Dario Russo
2
Ele quebra quando um parâmetro vazio é passado. Você deve procurar por$#
Sebi
bom testador de argumentos, concorde "", ''como argumento, também se não houver argumentos, é silencioso. Eu tentei consertar isso, mas precisa de um loop for e counter com $#. Acabei de adicionar isso no final:echo "End of args or received quoted null"
Merlin
8

Meu SUN Unix tem muitas limitações, mesmo "$ @" não foi interpretado como desejado. Minha solução alternativa é $ {@}. Por exemplo,

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

A propósito, eu tinha que ter esse script em particular porque meu Unix também não suporta grep -r

Hampden123
fonte
Esta é uma pergunta do Bash; você está usandoksh
tripleee 15/01
6

Se você incluir $@em uma sequência de caracteres citada com outros caracteres, o comportamento será muito estranho quando houver vários argumentos, apenas o primeiro argumento será incluído dentro das aspas.

Exemplo:

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

Rendimentos:

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

Mas atribuindo a uma variável diferente primeiro:

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

Rendimentos:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
fonte
3
Não vou negar que isso é desconcertante, mas na verdade faz sentido dentro da semântica do "$@"bash. Também ajuda a ilustrar a principal diferença entre $@e $*e por que os dois são úteis. Na bash(1)seção Parâmetros especiais da página de manual: " *- Quando a expansão ocorre entre aspas duplas, ela se expande para uma única palavra com o valor de cada parâmetro [...] Ou seja, "$*"é equivalente a "$1c$2c...", onde cestá [ $IFS]." E, de fato, usar em $*vez de $@no seu primeiro exemplo teria gerado uma saída idêntica à segunda versão.
FeRD 04/07/19
2
Agora, compare isso com "$@". Novamente na página do manual: " @- Quando a expansão ocorre entre aspas duplas, cada parâmetro se expande para uma palavra separada. Ou seja, "$@"é equivalente a "$1" "$2"... Se a expansão entre aspas duplas ocorrer dentro de uma palavra, a expansão do primeiro parâmetro será unida com a parte inicial da palavra original e a expansão do último parâmetro é associada à última parte da palavra original. " ... E, de fato, se o seu código estiver bash -c "true foo $@ bar baz", execute-o como test.sh one twoseria líquido bash -c 'true foo one' 'two bar baz'.
FeRD 04/07/19
1
Obrigado pela citação e informações sobre os documentos $*, parece que esqueço que ele existe. #
ColinM
Heh. Eu sou o oposto, $@estava ganhando força quando comecei o script de shell, ainda preciso me lembrar de que está lá. Era comum ver "$*"usado em scripts ... então o autor percebia que estava quebrando todos os argumentos juntos, para que tentassem todo tipo de bobagem complexa com a divisão de palavras "$*"ou [re] montando uma lista de argumentos fazendo um loop sobre shifta puxá-los um por um ... apenas usando $@resolve. (Ajuda a que o bash usa o mesmo mnemônico para os membros da matriz de acesso, também: ${var[*]}para todos eles como uma palavra, ${var[@]}para uma lista de palavras.)
Ferd
O verdadeiro problema aqui é que você está usando bash -cde uma maneira que não faz absolutamente nenhum sentido.
tripleee 15/01
4

Às vezes, você deseja passar todos os seus argumentos, mas precedido por uma bandeira (por exemplo --flag)

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

Você pode fazer isso da seguinte maneira:

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

Nota: para evitar a divisão extra do campo, é necessário citar %se $@, e para evitar ter uma única sequência, não é possível citar o subshell de printf.

kvantour
fonte
3

Funciona bem, exceto se você tiver espaços ou caracteres de escape. Não encontro uma maneira de capturar argumentos neste caso e enviar para um ssh dentro do script.

Isso pode ser útil, mas é tão feio

_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
fonte
3

Muitas respostas aqui recomendam $@ou $*com e sem aspas, no entanto, nenhuma parece explicar o que elas realmente fazem e por que você deveria fazer isso. Então, deixe-me roubar este excelente resumo desta resposta :

insira a descrição da imagem aqui

Observe que as aspas fazem toda a diferença e, sem as duas, têm um comportamento idêntico.

Para o meu propósito, eu precisava passar parâmetros de um script para outro como está e, para isso, a melhor opção é:

# 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 sem aspas e $@deve funcionar bem na situação acima.

Shital Shah
fonte
2

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

Observe que as aspas são importantes!

"$@", $@, "$*"Ou $*vai cada comportam um pouco diferente em relação escapar e concatenação como descrito nesta resposta stackoverflow .

Um caso de uso estreitamente relacionado está passando todos os argumentos fornecidos dentro de um argumento como este:

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

Eu uso uma variação da resposta do @ kvantour para conseguir isso:

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

Davidiusdadi
fonte