0

I'm using the special-case variable $@ to pass arguments from the user into a defined function in a shell script. The Bash manual states that for $@:

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.

This leads me to believe I can simply enclose the variable in double quotes with other words. For example: "You said: $@". However, this doesn't appear to work as I expected.

I've put together a small shell script to help debug an issue I was having with passing arguments using $@.

#!/usr/bin/env bash
showArgs() {
  while [ "$1" != "" ]; do
    echo "Arg: $1"
    shift
  done
}
VAR1="Quoted variable"
VAR2="Unquoted variable"
echo 1
showArgs "Quoted hardcoded"
echo 2
showArgs Unquoted hardcoded
echo 3
showArgs "$VAR1"
echo 4
showArgs $VAR2
echo 5
showArgs $@
echo 6
showArgs "$@"

Invoking it with ./showArgs Foo bar "bax qux", each case shows up as I'd expect. However, I'd expect case #6 to be "Foo bar bax qux" as a single argument since it's being passed to the function in double-quotes.

I don't fully understand why and haven't been able to find the answer. Insight would be appreciated!

SnoFox
  • 1
  • 1
  • What's up with your shebang? – John Goofy Aug 03 '17 at 19:53
  • It acts **exactly** like other array variables. – Charles Duffy Aug 03 '17 at 19:55
  • That is to say, if you set `array=( "first argument" "second argument" )`, you'll get comparable behavior comparing `"${array[@]}"` against `"${array[@]}`, `${array[*]}` and `"${array[*]}"`. – Charles Duffy Aug 03 '17 at 19:56
  • @JohnGoofy Some systems may put `bash` in a nonstandard location, or have two versions of `bash` in different places (like my Mac, with system default Bash v3.2, and homebrew Bash v4.2). Using `#!/usr/bin/env bash` means that the correct `bash` will be taken from `$PATH`, and the location of `env` is a lot more stable than `bash`. – HTNW Aug 03 '17 at 19:56
  • @CharlesDuffy I don't think this question counts as a duplicate. That question is about what `"$@"` generally means, but this one is about the details of why it acts the way it does with the whole word splitting thing. – HTNW Aug 03 '17 at 19:59
  • 1
    @HTNW Yes, of course, but there is no `!` in the shebang. – John Goofy Aug 03 '17 at 20:00
  • 1
    A typo ;P Keen eye. – HTNW Aug 03 '17 at 20:00
  • 1
    run your script `script A B "C D"` and clearly shows the difference – Diego Torres Milano Aug 03 '17 at 20:02
  • @HTNW, even if it's not a duplicate, inasmuch as it doesn't show how the OP's script is called (and the exact arguments are critical to understanding the difference), it's lacking a [mcve]. And I'm quite certain it *is* a duplicate -- though there might be a different question it's closer to. – Charles Duffy Aug 03 '17 at 20:03
  • @HTNW, ...I've edited to add another to the dupe list that's more on-point. – Charles Duffy Aug 03 '17 at 20:04
  • @CharlesDuffy Oh, ok. – HTNW Aug 03 '17 at 20:05
  • I've updated the question with the invocation Diego had mentioned, which changed the wording I used originally. The outputs of #5 and #6 are no longer identical. Charles' information about arrays helps clarify that $@ acts similar to an array, but I still don't know why arrays don't lose their word separation when sent along entirely in double quotes. (and I fixed the shebang :P) – SnoFox Aug 03 '17 at 20:48
  • @SnoFox, arrays would be useless if that were the behavior -- you couldn't store multi-word strings in them and restore those strings intact. Go back to the `array=( "first argument" "second argument" )` case. `for item in ${array[*]}` will set `item=first` and then `item=argument`, so that won't work. `for item in "${array[*]}"` will set `item="first argument second argument"`, so that won't work either. You **need** special-cased syntax, and that's what `@` is. – Charles Duffy Aug 04 '17 at 13:19
  • See the `Arrays` section of `man bash`, which describes this special case. You want the paragraph starting with the following text: `Any element of an array may be referenced using ${name[subscript]}. The braces are required to avoid conflicts with pathname expansion. If subscript is @ or *, the word expands to all members of name.` – Charles Duffy Aug 04 '17 at 13:21
  • Also, see the POSIX sh specification, which describes this behavior for `"$@"` specifically: http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02 (see the `@` entry in the Special Parameters section). – Charles Duffy Aug 04 '17 at 13:22

0 Answers0