Propagate all arguments in a bash shell script
Solution 1
Use "$@"
instead of plain $@
if you actually wish your parameters to be passed the same.
Observe:
$ cat no_quotes.sh
#!/bin/bash
echo_args.sh $@
$ cat quotes.sh
#!/bin/bash
echo_args.sh "$@"
$ cat echo_args.sh
#!/bin/bash
echo Received: $1
echo Received: $2
echo Received: $3
echo Received: $4
$ ./no_quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./no_quotes.sh "one quoted arg"
Received: one
Received: quoted
Received: arg
Received:
$ ./quotes.sh first second
Received: first
Received: second
Received:
Received:
$ ./quotes.sh "one quoted arg"
Received: one quoted arg
Received:
Received:
Received:
Solution 2
For bash and other Bourne-like shells:
java com.myserver.Program "$@"
Solution 3
Use "$@"
(works for all POSIX compatibles).
[...] , bash features the "$@" variable, which expands to all command-line parameters separated by spaces.
From Bash by example.
Solution 4
I realize this has been well answered but here's a comparison between "$@" $@ "$*" and $*
Contents of test script:
# 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 "================================="
Now, run the test script with various 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
=================================
Solution 5
A lot answers here recommends $@
or $*
with and without quotes, however none seems to explain what these really do and why you should that way. So let me steal this excellent summary from this answer:
+--------+---------------------------+
| Syntax | Effective result |
+--------+---------------------------+
| $* | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| $@ | $1 $2 $3 ... ${N} |
+--------+---------------------------+
| "$*" | "$1c$2c$3c...c${N}" |
+--------+---------------------------+
| "$@" | "$1" "$2" "$3" ... "${N}" |
+--------+---------------------------+
Notice that quotes makes all the difference and without them both have identical behavior.
For my purpose, I needed to pass parameters from one script to another as-is and for that the best option is:
# 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 $*
Notice no quotes and $@
should work as well in above situation.
Fragsworth
Developer of Clicker Heroes, Cloudstone, and other games http://www.clickerheroes.com/ http://www.kongregate.com/games/nexoncls/cloudstone http://armorgames.com/cloudstone-game/15364
Updated on July 08, 2022Comments
-
Fragsworth about 2 years
I am writing a very simple script that calls another script, and I need to propagate the parameters from my current script to the script I am executing.
For instance, my script name is
foo.sh
and callsbar.sh
foo.sh:
bar $1 $2 $3 $4
How can I do this without explicitly specifying each parameter?
-
blong about 10 years
-
Vadzim over 8 years
-
forresthopkinsa almost 3 yearsI had to use this solution for my use case
-
-
Chris Johnsen about 14 years@Amir: No, not for csh. For everyone’s sanity: do not script csh. But I think maybe
$argv:q
will work in some csh variants. -
Andy Thomas over 12 yearsThanks! As desired, this passes the same set of arguments received by the script -- not one big argument. The double-quotes are necessary. Works even if with quoted arguments that include spaces.
-
Mark Edington over 10 yearsThis does this work for pure pass through of quoted/escaped strings: Observe: cat rsync_foo.sh #!/bin/bash echo "$@" rsync "$@" ./rsync_foo.sh -n "bar me" bar2 bar me bar2skipping directory bar me Is it possible to have shell script that can see the true original command line complete with quotes or escaped strings?
-
Nathan Long over 10 yearsWhat about passing flags? Eg, './bar.sh --with-stuff'
-
greggo over 9 yearsRelated: if your shell script is just acting as a wrapper to run java, consider making the last line
exec java com.myserver.Program "$@"
This causes bash to exec into java, rather than wait around for it to complete. So, you are using one less process slot. Also, if the parent process (which ran your script) is watching it via the pid, and expecting it to be the 'java' process, some unusual things could break if you don't do an exec; the exec causes java to inherit the same pid. -
ben over 9 yearsSo is "$@" not just quotes around $@ but in effect a different built in variable?
-
Dario Russo over 9 yearsthis definitely did not help answer his question but this is indeed useful. upvoted!
-
dragonxlwang over 8 yearsfor some reason, I need to first do something like args="$@", first but then using $args would be treated as one single string? How to split it easily?
-
Chris Johnsen over 8 years@dragonxlwang: You could use an array variable, if your shell supports them (e.g. bash, zsh, others, but not plain Bourne- or POSIX-shell): save to args with
args=("$@")
and expand each element as a separate shell “word” (akin to"$@"
) with"${args[@]}"
. -
dragonxlwang over 8 yearsthanks @Chris, I am using zsh and it turns out by default it does not split string. To split it, expansion flag (${(s: :)args} is needed, or eval will do the trick
-
tripleee over 8 years@ben It is a single variable but it requires the double quotes around it to have a useful value distinct from the (broken)
$*
. I believe there is historical progression here;$*
did not work as designed, so$@
was invented to replace it; but the quoting rules being what they are, the double quotes around it are still required (or it will revert to the broken$*
semantics). -
Stuart Berg over 8 years"So is "$@" not just quotes around $@ but in effect a different built in variable?" -- For all intents and purposes, yes: stackoverflow.com/a/28099707/162094
-
Rany Albeg Wein over 8 yearsI suggest anyone who wants to understand the subject of Word Splitting better, to read more about it here.
-
IQAndreas over 8 yearsAre there any "gotchas" to keep in mind when using
"$@"
, like will it fail if you have escaped spaces in an argument, or null characters, or other special characters? -
Sebi over 7 yearsIt breaks when an empty parameter is passed. You should check for
$#
-
Michael Scheper about 7 yearsI suggest you show what happens when including an
'arg with spaces'
for the three examples, too. I was surprised by the results; hopefully you can explain them. -
Charles Duffy almost 7 years@MichaelScheper, what's surprising? Quote parsing happens before parameter expansion, which happens before string splitting. Thus, literal quotes in the results of parameter expansions aren't honored as syntax. Whereas in the case of
"$@"
you're not performing string-splitting after expansion at all. Anyhow, we've already got Q&A entries explaining all of this in detail; BashFAQ #50 is a good related read. -
Charles Duffy almost 7 years@MichaelScheper, anyhow,
./foo.sh "arg with spaces"
and./foo.sh 'arg with spaces'
are 100% identical, so I don't see how the suggestion of adding it to the examples given would be of any help. -
Michael Scheper almost 7 yearsHmmm... I just tried it again, and it's indeed behaving as I would expect and as you say. I don't know what I was doing last time I tried; I thought I copied your code verbatim. Thanks for checking.
-
isarandi about 6 yearsIf you call a script containing
echo "$@"
as./script.sh a "b c" d
then you just geta b c d
instead ofa "b c" d
, which is very much different. -
xiaobing about 6 yearsWhat if you pass option parameters, like
-e
, in my case, theecho
cmd will consume it? -
FeRD about 6 yearsI won't deny that's disconcerting, but it actually makes sense within the semantics of
"$@"
in bash. It also helps illustrate the key difference between$@
and$*
, and why they're both useful. From thebash(1)
man page Special Parameters section: "*
— When the expansion occurs within double quotes, it expands to a single word with the value of each parameter […] That is,"$*"
is equivalent to"$1c$2c..."
, wherec
is [$IFS
]." And indeed, using$*
instead of$@
in your first example would've netted output identical to the second version. -
FeRD about 6 yearsNow, compare that to
"$@"
. Again from the man page: "@
— When the expansion occurs within double quotes, each parameter expands to a separate word. That is,"$@"
is equivalent to"$1"
"$2"
… If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word." ...And indeed, if your code had beenbash -c "true foo $@ bar baz"
, then running it astest.sh one two
would netbash -c 'true foo one' 'two bar baz'
. -
ColinM almost 6 yearsThanks for the docs citation and info about
$*
, I seem to forget that it exists.. -
FeRD almost 6 yearsHeh. I'm the opposite,
$@
was just gaining traction when I first started shell scripting, I still have to remind myself it's there. It was common to see"$*"
used in scripts... then the author would realize it was smashing all of their arguments together, so they'd try all manner of complex nonsense with word-splitting"$*"
, or [re]assembling an arg list by looping overshift
to pull them down one by one... just using$@
solves it. (Helps that bash uses the same mnemonic to access array members, too:${var[*]}
for them all as a word,${var[@]}
for a list of words.) -
FeRD almost 6 years@isarandi While it's true that the output no longer contains the quotes, that's what you would expect... but rest assured that within the script,
echo
receives three arguments:"a" "b c" "d"
(then the shell joins them together as part of its string expansion). But if you'd usedfor i in "$@"; do echo $i; done
You'd have gottena⏎b c⏎d
. -
FeRD almost 6 years@tripleee is basically correct about that progression,
$@
was introduced to work around the problem that bare$*
was dangerous since arguments could contain spaces, but"$*"
was no solution because it ran them all together. (The previous solution was nonsense likewhile [ "$#" -gt 0 ]; do MYARG="$1"; shift; something "$MYARG"; done
to replace what could be justfor MYARG in "$@"; do something "$MYARG"; done
.) -
IanS over 5 yearsShouldn't
baz.sh "$@"
be./baz.sh "$@"
in the parent shell script? -
tripleee over 4 yearsThe real problem here is that you are using
bash -c
in a way which makes absolutely no sense. -
tripleee over 4 yearsThis is a Bash question; you are using
ksh
-
Merlin over 4 yearsgood args tester, agree on
""
,''
as an argument, also if there were no args it is silent. I tried to fix this, but needs a for loop and counter with$#
. I just added this on the end:echo "End of args or received quoted null"
-
Shital Shah about 4 yearsIt would have been to have explained what does these really do instead of having to guess from examples. stackoverflow.com/a/28099707/207661
-
Tim over 3 yearsThank you! Concise and clearly explains all options.
-
tripleee about 3 years@IQAndreas Depends on what you are hoping to accomplish exactly. For the trivial use case "I want to pass on exactly what I received",
"$@"
works fabulously and doesn't require anything more. -
Valerio almost 3 yearsI found java is off topic related to the question
-
user2846495 over 2 yearsYou can see this for yourself using
env --debug
. E.g. putenv --debug echo "$*"
inside a function and try executing it with different arguments. -
bibermann about 2 yearsYour example is wrong.
$*
passem them not as-is. Maybe it depends on how you define "as-is". Example: If you call./parent.sh 'a b' c
, then in the parent script$1
would eval toa b
but in the child script$1
would eval to onlya
. So this is not what I expect. I expect that both scripts "see" the same arguments and this only works with./child.sh "$@"
.