Difference Between $@ and $* in Bash Script

What is the difference between $@ and $* in shell scripts?

From here:

$@ behaves like $* except that when quoted the arguments are broken up properly if there are spaces in them.

Take this script for example (taken from the linked answer):

for var in "$@"
do
echo "$var"
done

Gives this:

$ sh test.sh 1 2 '3 4'
1
2
3 4

Now change "$@" to $*:

for var in $*
do
echo "$var"
done

And you get this:

$ sh test.sh 1 2 '3 4'
1
2
3
4

(Answer found by using Google)

What is the difference between $* and $@

Aside from the difference as described in the technical documents, it is best shown using some examples:

Lets assume we have four shell scripts, test1.sh:

#!/bin/bash
rm $*

test2.sh:

#!/bin/bash
rm "$*"

test3.sh:

#!/bin/bash
rm $@

test4.sh:

#!/bin/bash
rm "$@"

(I am using rm here instead of echo, because with echo, one can not see the difference)

We call all of them with the following commandline, in a directory which is otherwise empty:

./testX.sh "Hello World" Foo Bar

For test1.sh and test3.sh, we receive the following output:

rm: cannot remove ‘Hello’: No such file or directory
rm: cannot remove ‘World’: No such file or directory
rm: cannot remove ‘Foo’: No such file or directory
rm: cannot remove ‘Bar’: No such file or directory

This means, the arguments are taken as a whole string, joined with spaces, and then reparsed as arguments and passed to the command. This is generally not helpful when forwarding arguments to another command.

With test2.sh, we get:

rm: cannot remove ‘Hello World Foo Bar’: No such file or directory

So we have the same as for test{1,3}.sh, but this time, the result is passed as one argument.

test4.sh has something new:

rm: cannot remove ‘Hello World’: No such file or directory
rm: cannot remove ‘Foo’: No such file or directory
rm: cannot remove ‘Bar’: No such file or directory

This implies that the arguments are passed in a manner equivalent to how they were passed to the the script. This is helpful when passing arguments to other commands.

The difference is subtle, but will bite you when passing arguments to commands which expect information at certain points in the command line and when spaces take part in the game. This is in fact a good example of one of the many pitfalls of most shells.

difference between $@ and $* in bash script

If you have a script foo.sh:

asterisk "$*"
at-sign "$@"

and call it with:

./foo.sh "a a" "b b" "c c"

it's equivalent to:

asterisk "a a b b c c"
at-sign "a a" "b b" "c c"

Without the quotes, they're the same:

asterisk $*
at-sign $@

would be equivalent to:

asterisk "a" "a" "b" "b" "c" "c"
at-sign "a" "a" "b" "b" "c" "c"

What is the difference between $@ and $* in shell script?

There are no difference between $* and $@, but there is a difference between "$@" and "$*".

$ cat 1.sh
mkdir "$*"

$ cat 2.sh
mkdir "$@"

$ sh 1.sh a "b c" d

$ ls -l
total 12
-rw-r--r-- 1 igor igor 11 mar 24 10:20 1.sh
-rw-r--r-- 1 igor igor 11 mar 24 10:20 2.sh
drwxr-xr-x 2 igor igor 4096 mar 24 10:21 a b c d

We gave three arguments to the script (a, b c and d) but in "$*" they all were merged into one argument a b c d.

$ sh 2.sh a "b c" d

$ ls -l
total 24
-rw-r--r-- 1 igor igor 11 mar 24 10:20 1.sh
-rw-r--r-- 1 igor igor 11 mar 24 10:20 2.sh
drwxr-xr-x 2 igor igor 4096 mar 24 10:21 a
drwxr-xr-x 2 igor igor 4096 mar 24 10:21 a b c d
drwxr-xr-x 2 igor igor 4096 mar 24 10:21 b c
drwxr-xr-x 2 igor igor 4096 mar 24 10:21 d

You can see here, that "$*" means always one single argument, and "$@" contains as many arguments, as the script had. "$@" is a special token which means "wrap each individual argument in quotes". So a "b c" d becomes (or rather stays) "a" "b c" "d" instead of "a b c d" ("$*") or "a" "b" "c" "d" ($@ or $*).

Also, I would recommend this beautiful reading on the theme:

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

Difference between $@ and $* in bash scripting

From http://tldp.org/LDP/abs/html/refcards.html:

"$*" All the positional parameters (as a single word) *

"$@" All the positional parameters (as separate strings)

This code shows it: given a string with items separated by spaces, $@ considers every word as a new item, while $* considers them all together the same parameter.

echo "Params for: \$@"
for item in "${@}"
do
echo $item --
done

echo "Params for : \$*"
for item in "${*}"
do
echo $item --
done

Test:

$ ./a par1 par2 par3
Your command line contains 3 arguments
Params for: $@
par1 --
par2 --
par3 --
Params for : $*
par1 par2 par3 --

The difference between $* and $@

Unquoted, there is no difference -- they're expanded to all the arguments and they're split accordingly. The difference comes when quoting. "$@" expands to properly quoted arguments and "$*" makes all arguments into a single argument. Take this for example:

#!/bin/bash

function print_args_at {
printf "%s\n" "$@"
}

function print_args_star {
printf "%s\n" "$*"
}

print_args_at "one" "two three" "four"
print_args_star "one" "two three" "four"

Then:

$ ./printf.sh 

one
two three
four

one two three four

Difference between ${} and $() in Bash

The syntax is token-level, so the meaning of the dollar sign depends on the token it's in. The expression $(command) is a modern synonym for `command` which stands for command substitution; it means run command and put its output here. So

echo "Today is $(date). A fine day."

will run the date command and include its output in the argument to echo. The parentheses are unrelated to the syntax for running a command in a subshell, although they have something in common (the command substitution also runs in a separate subshell).

By contrast, ${variable} is just a disambiguation mechanism, so you can say ${var}text when you mean the contents of the variable var, followed by text (as opposed to $vartext which means the contents of the variable vartext).

The while loop expects a single argument which should evaluate to true or false (or actually multiple, where the last one's truth value is examined -- thanks Jonathan Leffler for pointing this out); when it's false, the loop is no longer executed. The for loop iterates over a list of items and binds each to a loop variable in turn; the syntax you refer to is one (rather generalized) way to express a loop over a range of arithmetic values.

A for loop like that can be rephrased as a while loop. The expression

for ((init; check; step)); do
body
done

is equivalent to

init
while check; do
body
step
done

It makes sense to keep all the loop control in one place for legibility; but as you can see when it's expressed like this, the for loop does quite a bit more than the while loop.

Of course, this syntax is Bash-specific; classic Bourne shell only has

for variable in token1 token2 ...; do

(Somewhat more elegantly, you could avoid the echo in the first example as long as you are sure that your argument string doesn't contain any % format codes:

date +'Today is %c. A fine day.'

Avoiding a process where you can is an important consideration, even though it doesn't make a lot of difference in this isolated example.)

Difference between $@ and $* when passing arguments to bash function

There is no difference between $* and $@. Both of them result in the list of arguments being glob-expanded and word-split, so you no longer have any idea about the original arguments. You hardly ever want this.

"$*" results in a single string, which is all of the arguments joined using the first character of $IFS as a separator (by default, a space). This is occasionally what you want.

"$@" results in one string per argument, neither word-split nor glob-expanded. This is usually what you want.



Related Topics



Leave a reply



Submit