Propagate all arguments in a bash shell script

568,293

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.

Share:
568,293
Fragsworth
Author by

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, 2022

Comments

  • Fragsworth
    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 calls bar.sh

    foo.sh:

    bar $1 $2 $3 $4
    

    How can I do this without explicitly specifying each parameter?

  • Chris Johnsen
    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
    Andy Thomas over 12 years
    Thanks! 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
    Mark Edington over 10 years
    This 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
    Nathan Long over 10 years
    What about passing flags? Eg, './bar.sh --with-stuff'
  • greggo
    greggo over 9 years
    Related: 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
    ben over 9 years
    So is "$@" not just quotes around $@ but in effect a different built in variable?
  • Dario Russo
    Dario Russo over 9 years
    this definitely did not help answer his question but this is indeed useful. upvoted!
  • dragonxlwang
    dragonxlwang over 8 years
    for 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
    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
    dragonxlwang over 8 years
    thanks @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
    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
    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
    Rany Albeg Wein over 8 years
    I suggest anyone who wants to understand the subject of Word Splitting better, to read more about it here.
  • IQAndreas
    IQAndreas over 8 years
    Are 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
    Sebi over 7 years
    It breaks when an empty parameter is passed. You should check for $#
  • Michael Scheper
    Michael Scheper about 7 years
    I 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
    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
    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
    Michael Scheper almost 7 years
    Hmmm... 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
    isarandi about 6 years
    If you call a script containing echo "$@" as ./script.sh a "b c" d then you just get a b c d instead of a "b c" d, which is very much different.
  • xiaobing
    xiaobing about 6 years
    What if you pass option parameters, like -e, in my case, the echo cmd will consume it?
  • FeRD
    FeRD about 6 years
    I 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 the bash(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...", where c is [$IFS]." And indeed, using $* instead of $@ in your first example would've netted output identical to the second version.
  • FeRD
    FeRD about 6 years
    Now, 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 been bash -c "true foo $@ bar baz", then running it as test.sh one two would net bash -c 'true foo one' 'two bar baz'.
  • ColinM
    ColinM almost 6 years
    Thanks for the docs citation and info about $*, I seem to forget that it exists..
  • FeRD
    FeRD almost 6 years
    Heh. 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 over shift 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
    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 used for i in "$@"; do echo $i; done You'd have gotten a⏎b c⏎d.
  • FeRD
    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 like while [ "$#" -gt 0 ]; do MYARG="$1"; shift; something "$MYARG"; done to replace what could be just for MYARG in "$@"; do something "$MYARG"; done.)
  • IanS
    IanS over 5 years
    Shouldn't baz.sh "$@" be ./baz.sh "$@" in the parent shell script?
  • tripleee
    tripleee over 4 years
    The real problem here is that you are using bash -c in a way which makes absolutely no sense.
  • tripleee
    tripleee over 4 years
    This is a Bash question; you are using ksh
  • Merlin
    Merlin over 4 years
    good 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
    Shital Shah about 4 years
    It would have been to have explained what does these really do instead of having to guess from examples. stackoverflow.com/a/28099707/207661
  • Tim
    Tim over 3 years
    Thank you! Concise and clearly explains all options.
  • tripleee
    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
    Valerio almost 3 years
    I found java is off topic related to the question
  • user2846495
    user2846495 over 2 years
    You can see this for yourself using env --debug. E.g. put env --debug echo "$*" inside a function and try executing it with different arguments.
  • bibermann
    bibermann about 2 years
    Your 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 to a b but in the child script $1 would eval to only a. So this is not what I expect. I expect that both scripts "see" the same arguments and this only works with ./child.sh "$@".