Propaguj wszystkie argumenty w skrypcie powłoki bash

849

Piszę bardzo prosty skrypt, który wywołuje inny skrypt, i muszę propagować parametry z bieżącego skryptu do skryptu, który wykonuję.

Na przykład moja nazwa skryptu to foo.shi wywołaniabar.sh

foo.sh:

bar $1 $2 $3 $4

Jak mogę to zrobić bez wyraźnego określenia każdego parametru?

Fragsworth
źródło

Odpowiedzi:

1403

Użyj "$@"zamiast zwykłego, $@jeśli faktycznie chcesz, aby parametry były takie same.

Przestrzegać:

$ 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
źródło
7
Działa to dla czystego przejścia ciągów cytowanych / uciekających: Obserwuj: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping katalog bar me Czy jest możliwe, aby skrypt powłoki widział prawdziwą oryginalną linię poleceń wraz z cudzysłowami lub znakami ucieczki?
Mark Edington
3
Co powiesz na przekazywanie flag? Np. „./Bar.sh --with-stuff”
Nathan Long
4
Sugeruję każdemu, kto chce lepiej zrozumieć temat podziału słów, aby przeczytać więcej na ten temat tutaj.
Rany Albeg Wein
4
Sugeruję, abyś pokazał, co się stanie, włączając również 'arg with spaces'trzy przykłady. Byłem zaskoczony wynikami; mam nadzieję, że potrafisz je wyjaśnić.
Michael Scheper
2
@MichaelScheper, w każdym razie, ./foo.sh "arg with spaces"i ./foo.sh 'arg with spaces'są w 100% identyczne, więc nie rozumiem, w jaki sposób sugestia dodania go do podanych przykładów byłaby pomocna.
Charles Duffy,
475

W przypadku bash i innych powłok podobnych do Bourne'a:

java com.myserver.Program "$@"
Chris Johnsen
źródło
13
@Amir: Nie, nie dla csh . Dla zdrowia psychicznego: nie pisz w języku csh . Ale myślę, że może $argv:qzadziała w niektórych wariantach csh.
Chris Johnsen,
2
Dzięki! Zgodnie z życzeniem przekazuje ten sam zestaw argumentów otrzymany przez skrypt - nie jeden duży argument. Podwójne cudzysłowy są konieczne. Działa, nawet jeśli z cytowanymi argumentami zawierającymi spacje.
Andy Thomas
24
Powiązane: jeśli twój skrypt powłoki działa tylko jako opakowanie do uruchomienia Java, rozważ utworzenie ostatniej linii. exec java com.myserver.Program "$@"Powoduje to, że bash wykona polecenie Java, zamiast czekać na zakończenie. Tak więc korzystasz z jednego mniejszego gniazda procesu. Ponadto, jeśli proces nadrzędny (który uruchomił skrypt) obserwuje go przez pid i oczekuje, że będzie to proces „java”, niektóre nietypowe rzeczy mogą się zepsuć, jeśli nie wykonasz exec; exec powoduje, że java dziedziczy ten sam pid.
greggo
7
@dragonxlwang: Możesz użyć zmiennej tablicowej, jeśli twoja powłoka je obsługuje (np. bash , zsh , inne, ale nie zwykła powłoka Bourne'a lub POSIX): zapisz do argumentów za pomocą args=("$@")i rozwiń każdy element jako osobne „słowo” powłoki ( pokrewny "$@") z "${args[@]}".
Chris Johnsen,
1
Czy są jakieś „gotchas”, o których należy pamiętać podczas używania "$@", tak jak nie powiedzie się, jeśli spacja w argumencie, znaki zerowe lub inne znaki specjalne zawiodą?
IQAndreas,
97

Użyj "$@"(działa dla wszystkich kompatybilnych z POSIX ).

[...] bash zawiera zmienną „$ @”, która rozwija się do wszystkich parametrów wiersza poleceń oddzielonych spacjami.

Z Bash przez przykład .

miku
źródło
6
Czy więc „$ @” nie tylko cytuje około $ @, ale w efekcie inną wbudowaną zmienną?
ben
5
@ben Jest to pojedyncza zmienna, ale wymaga, aby podwójne cudzysłowy wokół niej miały użyteczną wartość inną niż (zepsuta) $*. Wierzę, że tutaj jest historyczny postęp; $*nie działał zgodnie z przeznaczeniem, dlatego $@został wymyślony, aby go zastąpić; ale reguły cytowania są takie, jakie są, podwójne cytaty wokół niego są nadal wymagane (w przeciwnym razie nastąpi powrót do zepsutej $*semantyki).
tripleee
7
„Czy więc„ $ @ ”nie tylko cytuje około $ @, ale w rzeczywistości inną wbudowaną zmienną?” - Do wszystkich celów i celów, tak: stackoverflow.com/a/28099707/162094
Stuart Berg
1
Jeśli wywołujesz skrypt zawierający echo "$@"jako, ./script.sh a "b c" dto po prostu dostajesz a b c dzamiast a "b c" d, co jest bardzo różne.
isarandi
7
@isarandi Chociaż prawdą jest, że dane wyjściowe nie zawierają już cudzysłowów, tego można się spodziewać ... ale zapewniamy, że w skrypcie echootrzymujemy trzy argumenty: "a" "b c" "d"(wtedy powłoka łączy je razem w ramach rozszerzenia łańcucha). Ale gdybyś użył for i in "$@"; do echo $i; done, dostałbyś a⏎b c⏎d.
FeRD
64

Zdaję sobie sprawę, że dobrze na nie odpowiedziano, ale oto porównanie między „$ @” $ @ „$ *” i $ *

Zawartość skryptu testowego:

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

Teraz uruchom skrypt testowy z różnymi argumentami:

# ./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
źródło
1
dobra zwięzła demonstracja i wkład w tę odpowiedź.
Merlin
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Pomyślałem, że może to być nieco bardziej przydatne podczas próby sprawdzenia, w jaki sposób argony wchodzą do twojego skryptu

Gregory Patmore
źródło
6
to zdecydowanie nie pomogło odpowiedzieć na jego pytanie, ale jest to rzeczywiście przydatne. entuzjastycznie!
Dario Russo,
2
Łamie się, gdy przekazywany jest pusty parametr. Powinieneś sprawdzić$#
Sebi
dobry tester argumentów, zgódź się "", ''jako argument, także jeśli nie było argumentów, milczy. Próbowałem to naprawić, ale potrzebuję pętli for i przeciwdziałam $#. Właśnie dodałem to na końcu:echo "End of args or received quoted null"
Merlin
8

Mój SUN Unix ma wiele ograniczeń, nawet „$ @” nie było interpretowane jako pożądane. Moje obejście to {@}. Na przykład,

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

Nawiasem mówiąc, musiałem mieć ten konkretny skrypt, ponieważ mój Unix również nie obsługuje grep -r

Hampden123
źródło
To jest pytanie Bash; używaszksh
tripleee
6

Jeśli uwzględnisz $@ w cudzysłowie zostanie dołączony ciąg znaków, zachowanie jest bardzo dziwne, gdy występuje wiele argumentów, tylko pierwszy argument jest zawarty w cudzysłowie.

Przykład:

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

Wydajność:

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

Ale najpierw przypisując inną zmienną:

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

Wydajność:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
źródło
3
Nie zaprzeczę, że jest to niepokojące, ale w rzeczywistości ma sens w semantyce "$@"bash. Pomaga również zilustrować kluczową różnicę między $@i $*, i dlaczego oba są przydatne. Ze strony podręcznika bash(1)sekcja Parametry specjalne: „ *- Gdy rozwinięcie występuje w obrębie podwójnych cudzysłowów, rozwija się do pojedynczego słowa o wartości każdego parametru […] Oznacza to, że "$*"jest równoważne "$1c$2c...", gdzie cjest [ $IFS].” I rzeczywiście, użycie $*zamiast $@w pierwszym przykładzie dałoby wynik netto identyczny jak w drugiej wersji.
FeRD
2
Porównaj to teraz "$@". Ponownie ze strony podręcznika: „ @- Gdy interpretacja występuje w obrębie podwójnych cudzysłowów, każdy parametr jest interpretowany jako osobne słowo. To znaczy "$@"jest równoważne "$1" "$2"… Jeśli podwójny cudzysłów występuje w danym słowie, interpretacja pierwszego parametru jest łączona z początkową częścią oryginalnego słowa, a rozwinięcie ostatniego parametru jest połączone z ostatnią częścią oryginalnego słowa. ” ... I rzeczywiście, jeśli twój kod był bash -c "true foo $@ bar baz", to uruchom go tak, jak w test.sh one twosieci bash -c 'true foo one' 'two bar baz'.
FeRD
1
Dzięki za cytowanie dokumentów i informacje o $*, wydaje mi się, że zapominam, że istnieje ..
ColinM
Heh Przeciwnie, $@zaczynałem zyskiwać na popularności, kiedy zacząłem pisać skrypty powłoki, wciąż muszę sobie przypominać, że tam jest. Często "$*"używano ich w skryptach ... wtedy autor zdawał sobie sprawę, że rozbijał wszystkie ich argumenty, więc próbowali wszelkiego rodzaju złożonych bzdur z podziałem słów "$*"lub [ponownie] składali listę argumentów przez zapętlanie ponad shiftciągnąć je jeden po drugim ... tylko za pomocą $@rozwiązuje go. (Pomaga temu bashowi używać również tego samego mnemonika, aby uzyskać dostęp do członków tablicy: ${var[*]}dla nich wszystkich jako słowa, ${var[@]}dla listy słów.)
FeRD
Prawdziwy problem polega na tym, że używasz go bash -cw sposób, który nie ma absolutnie żadnego sensu.
tripleee
4

Czasami chcesz przekazać wszystkie argumenty, ale poprzedza je flaga (np. --flag)

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

Możesz to zrobić w następujący sposób:

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

Uwaga: aby uniknąć dodatkowego podziału pola, musisz zacytować %si $@, aby uniknąć pojedynczego łańcucha, nie możesz zacytować podpowłoki printf.

kvantour
źródło
3

Działa dobrze, chyba że masz spacje lub znaki ucieczki. W tym przypadku nie znajduję sposobu na przechwycenie argumentów i wysłanie do ssh w skrypcie.

Może to być przydatne, ale jest tak brzydkie

_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
źródło
3

Wiele odpowiedzi tutaj poleca $@lub $*z cytatami i bez, jednak wydaje się, że żadna nie wyjaśnia, co one naprawdę robią i dlaczego powinieneś w ten sposób. Pozwól mi więc ukraść to doskonałe streszczenie z tej odpowiedzi :

wprowadź opis zdjęcia tutaj

Zauważ, że cytaty mają znaczenie i bez nich oba mają identyczne zachowanie.

W moim celu musiałem przekazywać parametry z jednego skryptu do drugiego w obecnej postaci, a najlepszą opcją jest:

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

Zauważ, że nie ma cytatów i $@powinien również działać w powyższej sytuacji.

Shital Shah
źródło
2

bar "$@" będzie równoważne z bar "$1" "$2" "$3" "$4"

Zauważ, że znaki cudzysłowu są ważne!

"$@", $@, "$*"Lub $*każdy będzie zachowywać się nieco inaczej w sprawie ucieczki i łączenie w sposób opisany w niniejszym stackoverflow odpowiedź .

Jednym ściśle powiązanym przypadkiem użycia jest przekazanie wszystkich podanych argumentów w argumencie takim jak ten:

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

Używam odmiany odpowiedzi @ kvantour, aby to osiągnąć:

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

Davidiusdadi
źródło