Verbreiten Sie alle Argumente in einem Bash-Shell-Skript

851

Ich schreibe ein sehr einfaches Skript, das ein anderes Skript aufruft, und ich muss die Parameter von meinem aktuellen Skript auf das Skript übertragen, das ich ausführe.

Zum Beispiel ist mein Skriptname foo.shund ruft aufbar.sh

foo.sh:

bar $1 $2 $3 $4

Wie kann ich dies tun, ohne jeden Parameter explizit anzugeben?

Fragsworth
quelle

Antworten:

1406

Verwenden Sie "$@"statt einfach, $@wenn Sie tatsächlich möchten, dass Ihre Parameter gleich übergeben werden.

Beobachten:

$ 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
quelle
7
Dies funktioniert für den reinen Durchgang von Zeichenfolgen in Anführungszeichen / Escapezeichen: Beachten Sie: cat rsync_foo.sh #! / Bin / bash echo "$ @" rsync "$ @" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping-Verzeichnis bar me Ist es möglich, ein Shell-Skript zu haben, das die wahre ursprüngliche Befehlszeile mit Anführungszeichen oder maskierten Zeichenfolgen anzeigt?
Mark Edington
3
Was ist mit Flaggen? ZB './bar.sh --with-stuff'
Nathan Long
4
Ich empfehle jedem, der das Thema Wortaufteilung besser verstehen möchte, hier mehr darüber
Rany Albeg Wein
4
Ich schlage vor, Sie zeigen, was passiert, wenn Sie auch 'arg with spaces'für die drei Beispiele ein einfügen. Ich war von den Ergebnissen überrascht; hoffentlich kannst du sie erklären.
Michael Scheper
2
@MichaelScheper jedenfalls ./foo.sh "arg with spaces"und ./foo.sh 'arg with spaces'sind zu 100% identisch, daher sehe ich nicht, wie der Vorschlag, es zu den angegebenen Beispielen hinzuzufügen, hilfreich wäre.
Charles Duffy
475

Für Bash und andere Bourne-ähnliche Muscheln:

java com.myserver.Program "$@"
Chris Johnsen
quelle
13
@Amir: Nein, nicht für csh . Für jedermanns Verstand: Schreiben Sie kein Skript für csh . Aber ich denke, vielleicht $argv:qfunktioniert es in einigen csh-Varianten.
Chris Johnsen
2
Vielen Dank! Wie gewünscht werden dabei dieselben Argumente übergeben, die vom Skript empfangen wurden - kein einziges großes Argument. Die doppelten Anführungszeichen sind erforderlich. Funktioniert auch mit Anführungszeichen, die Leerzeichen enthalten.
Andy Thomas
24
Verwandte Themen: Wenn Ihr Shell-Skript nur als Wrapper zum Ausführen von Java fungiert, sollten Sie die letzte Zeile exec java com.myserver.Program "$@"erstellen. Dies führt dazu, dass Bash in Java ausgeführt wird, anstatt zu warten, bis es abgeschlossen ist. Sie verwenden also einen Prozesssteckplatz weniger. Wenn der übergeordnete Prozess (der Ihr Skript ausgeführt hat) es über die PID überwacht und erwartet, dass es sich um den Java-Prozess handelt, können einige ungewöhnliche Dinge beschädigt werden, wenn Sie keine Exec ausführen. Die Ausführung bewirkt, dass Java dieselbe PID erbt.
Greggo
7
@dragonxlwang: Sie könnten eine Array - Variable verwenden, wenn Ihre Shell unterstützt sie (zB bash , zsh , andere, aber nicht glatt Bourne- oder POSIX-Shell): Für mehr Details args mit args=("$@")und erweitern jedes Element als separate Schale „Wort“ ( ähnlich wie "$@") mit "${args[@]}".
Chris Johnsen
1
Gibt es irgendwelche "Fallstricke", die bei der Verwendung zu beachten "$@"sind. Wird dies fehlschlagen, wenn Sie Leerzeichen in einem Argument oder Nullzeichen oder andere Sonderzeichen maskiert haben?
IQAndreas
97

Verwendung "$@"(funktioniert für alle POSIX- kompatiblen Geräte ).

[...] enthält bash die Variable "$ @", die auf alle durch Leerzeichen getrennten Befehlszeilenparameter erweitert wird.

Von Bash mit gutem Beispiel .

miku
quelle
6
Ist "$ @" also nicht nur ein Anführungszeichen um $ @, sondern tatsächlich eine andere eingebaute Variable?
Ben
5
@ben Es ist eine einzelne Variable, aber es erfordert, dass die doppelten Anführungszeichen einen nützlichen Wert haben, der sich von dem (gebrochenen) unterscheidet $*. Ich glaube, hier gibt es historische Fortschritte. $*funktionierte nicht wie geplant, wurde also $@erfunden, um es zu ersetzen; Da die Anführungszeichen jedoch so sind, wie sie sind, sind die doppelten Anführungszeichen weiterhin erforderlich (oder es wird auf die fehlerhafte $*Semantik zurückgegriffen).
Tripleee
7
"Also ist" $ @ "nicht nur ein Anführungszeichen um $ @, sondern tatsächlich eine andere eingebaute Variable?" - In jeder Hinsicht ja: stackoverflow.com/a/28099707/162094
Stuart Berg
1
Wenn Sie ein Skript aufrufen, das echo "$@"as enthält, erhalten ./script.sh a "b c" dSie a b c dstattdessen statt a "b c" d, was sehr unterschiedlich ist.
Isarandi
7
@isarandi Es stimmt zwar, dass die Ausgabe nicht mehr die Anführungszeichen enthält, aber das ist, was Sie erwarten würden ... aber seien Sie versichert, dass das Skript echodrei Argumente erhält: "a" "b c" "d"(dann verbindet die Shell sie als Teil ihrer Zeichenfolgenerweiterung). Aber wenn du benutzt hättest, for i in "$@"; do echo $i; donehättest du bekommen a⏎b c⏎d.
FeRD
64

Mir ist klar, dass dies gut beantwortet wurde, aber hier ist ein Vergleich zwischen "$ @" $ @ "$ *" und $ *

Inhalt des Testskripts:

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

Führen Sie nun das Testskript mit verschiedenen Argumenten aus:

# ./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
quelle
1
gute prägnante Demonstration und Beitrag zu dieser Antwort.
Merlin
33
#!/usr/bin/env bash
while [ "$1" != "" ]; do
  echo "Received: ${1}" && shift;
done;

Ich dachte nur, dies könnte etwas nützlicher sein, wenn Sie versuchen zu testen, wie Argumente in Ihr Skript gelangen

Gregory Patmore
quelle
6
Dies hat definitiv nicht dazu beigetragen, seine Frage zu beantworten, aber dies ist in der Tat nützlich. upvoted!
Dario Russo
2
Es wird unterbrochen, wenn ein leerer Parameter übergeben wird. Sie sollten nach$#
Sebi
Guter Args-Tester, stimme zu "", ''als Argument, auch wenn es keine Args gab, ist es still. Ich habe versucht, dies zu beheben, brauche aber eine for-Schleife und einen Zähler mit $#. Ich habe dies gerade am Ende hinzugefügt:echo "End of args or received quoted null"
Merlin
8

Mein SUN Unix hat viele Einschränkungen, sogar "$ @" wurde nicht wie gewünscht interpretiert. Meine Problemumgehung ist $ {@}. Zum Beispiel,

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

Übrigens musste ich dieses spezielle Skript haben, da mein Unix auch grep -r nicht unterstützt

Hampden123
quelle
Dies ist eine Bash-Frage; Sie verwendenksh
Tripleee
6

Wenn Sie $@eine Zeichenfolge in Anführungszeichen mit anderen Zeichen einfügen, ist das Verhalten bei mehreren Argumenten sehr merkwürdig. In den Anführungszeichen ist nur das erste Argument enthalten.

Beispiel:

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

Ausbeuten:

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

Aber zuerst einer anderen Variablen zuweisen:

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

Ausbeuten:

$ bash test.sh bar baz
+ args='bar baz'
+ bash -c 'true foo bar baz'
ColinM
quelle
3
Ich werde nicht leugnen, dass das beunruhigend ist, aber es macht tatsächlich Sinn innerhalb der Semantik von "$@"in bash. Es hilft auch dabei, den Hauptunterschied zwischen $@und zu veranschaulichen $*und warum beide nützlich sind. Im bash(1)Abschnitt Spezielle Parameter auf der Manpage: " *- Wenn die Erweiterung in doppelten Anführungszeichen erfolgt, wird sie zu einem einzelnen Wort mit dem Wert jedes Parameters erweitert […] Das heißt, "$*"entspricht [ "$1c$2c..."wo cist [ $IFS]." In der Tat hätte die Verwendung $*anstelle von $@in Ihrem ersten Beispiel die Ausgabe identisch mit der zweiten Version gebracht.
FeRD
2
Vergleichen Sie das jetzt mit "$@". Nochmals aus der Manpage: " @- Wenn die Erweiterung in doppelten Anführungszeichen erfolgt, wird jeder Parameter zu einem separaten Wort erweitert. Dies "$@"entspricht "$1" "$2"... Wenn die Erweiterung in doppelten Anführungszeichen innerhalb eines Wortes erfolgt, wird die Erweiterung des ersten Parameters verbunden mit dem Anfangsteil des ursprünglichen Wortes, und die Erweiterung des letzten Parameters wird mit dem letzten Teil des ursprünglichen Wortes verbunden. " ... Und in der Tat, wenn Ihr Code gewesen wäre bash -c "true foo $@ bar baz", dann führen Sie ihn wie test.sh one twonet aus bash -c 'true foo one' 'two bar baz'.
FeRD
1
Vielen Dank für die Dokumentation Zitat und Informationen über $*, ich scheine zu vergessen, dass es existiert ..
ColinM
Heh. Ich bin das Gegenteil, habe $@gerade erst an Fahrt gewonnen, als ich mit dem Shell-Scripting angefangen habe. Ich muss mich immer noch daran erinnern, dass es da ist. Es war üblich, die "$*"Verwendung in Skripten zu sehen ... dann erkannte der Autor, dass alle ihre Argumente zusammengeschlagen wurden, sodass sie alle Arten von komplexem Unsinn mit Wortteilung "$*"versuchten oder eine Argumentliste durch Schleifen [neu] zusammenstellten rüber shift, um sie eins nach dem anderen herunterzuziehen ... nur mit $@löst es. (Hilft, dass Bash dieselbe Mnemonik verwendet, um auch auf Array-Mitglieder zuzugreifen: ${var[*]}für sie alle als Wort, ${var[@]}für eine Liste von Wörtern.)
FeRD
Das eigentliche Problem hierbei ist, dass Sie bash -cauf eine Weise verwenden, die absolut keinen Sinn ergibt .
Tripleee
4

Manchmal möchten Sie alle Ihre Argumente zu übergeben, aber voraus durch ein Flag (zB --flag)

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

Sie können dies folgendermaßen tun:

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

Hinweis: Um eine zusätzliche Feldaufteilung zu vermeiden, müssen Sie %sund zitieren. $@Um eine einzelne Zeichenfolge zu vermeiden, können Sie die Unterschale von nicht zitieren printf.

kvantour
quelle
3

Funktioniert einwandfrei, außer wenn Sie Leerzeichen oder maskierte Zeichen haben. Ich finde in diesem Fall keine Möglichkeit, Argumente zu erfassen und an ein SSH innerhalb des Skripts zu senden.

Dies könnte nützlich sein, ist aber so hässlich

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

Viele Antworten hier empfehlen $@oder $*mit und ohne Anführungszeichen, jedoch scheint keine zu erklären, was diese wirklich tun und warum Sie dies tun sollten. Lassen Sie mich diese hervorragende Zusammenfassung aus dieser Antwort stehlen :

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass Anführungszeichen den Unterschied ausmachen und ohne sie beide dasselbe Verhalten haben.

Für meinen Zweck musste ich Parameter von einem Skript an ein anderes übergeben, wie es ist, und dafür ist die beste Option:

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

Beachten Sie keine Anführungszeichen und $@sollten auch in der obigen Situation funktionieren.

Shital Shah
quelle
2

bar "$@" wird gleichbedeutend sein mit bar "$1" "$2" "$3" "$4"

Beachten Sie, dass die Anführungszeichen wichtig sind!

"$@", $@, "$*"Oder $*wird jeder verhalten sich etwas anders in Bezug auf Flucht und Verkettung wie in diesem beschrieben Stackoverflow Antwort .

Ein eng verwandter Anwendungsfall besteht darin , alle angegebenen Argumente in einem Argument wie diesem zu übergeben:

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

Ich benutze eine Variation von @ kvantours Antwort, um dies zu erreichen:

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

Davidiusdadi
quelle