Bash – Why does –text=“$@” only pass the first word

bashquotingshell

When I run the following script with some arguments like arg1 arg2 arg3:

#!/bin/bash
zenity --entry --text="$@"

zenity creates an entry dialog with this text: "arg1" whereas I expect "arg1 arg2 arg3"

If I use a variable like the following script it shows all arguments for the entry text.

#!/bin/bash
text="$@"
zenity --entry --text="$text"

What's the difference between these scripts? Why does the first one replace $@ with the first argument only?

Best Answer

$@ expands to separate words (whereas $* expands to a single word), as explained in the bash manual. Thus, when you write

zenity --text="$@"

it expands to

zenity --text="$1" "$2" "$3"

However, shell variable assignments do not undergo word splitting. Note that field / word splitting is omitted in the list of expansions for variable assignments in the bash manual. This behavior is consistent with the POSIX spec. So, when you write

text="$@"

the variable text gets all of the positional parameters as a single word, equivalent to if you had written text="$*". Indeed, this is the reason double quotes are often unnecessary in variable assignments. Both

text=$@

and

text=$*

are perfectly safe.

So,

text=$@
zenity --option="$text"

expands "$text" to a single word, which is why this works. Note that the --option="$@" is just a normal argument to the command zenity, and not a shell variable assignment, which is why word splitting takes place here but not in text=$@.

Related Question