-1

I get confused on array syntax in bash. I typically use quotes to enclose strings to loop through, but tutorials typically use parentheses. For kicks I made 4 simple loops. The first two work as I expected by echoing each item in the array, the second two gave unexpected output.

Why does this happen?

LOOP #1

$ List="item1 item2 item3"
$ for f in $List; do echo ${f}; done
item1
item2
item3

LOOP #2

$ List=(item1 item2 item3)
$ for f in ${List[@]}; do echo ${f}; done
item1
item2
item3

LOOP #3

$ List="item1 item2 item3"
$ for f in ${List[@]}; do echo ${f}; done
item1
item2
item3
item2
item3

LOOP #4

$ List=(item1 item2 item3)
$ for f in $List; do echo ${f}; done
item1
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
bigdoyle
  • 9
  • 1
  • 1
    *Always* quote `${List[@]}`, as otherwise there is no reason to use `@` instead of `*`. – chepner May 04 '18 at 19:22
  • The first and 3rd don't have any array involved *at all*. Unquoted string expansion != array. – Charles Duffy May 04 '18 at 19:35
  • ...also, you've got a bunch of quoting issues here; http://shellcheck.net/ will catch them automatically, and provide wiki links with explanations. – Charles Duffy May 04 '18 at 19:39
  • @Charles. I think your comments get at the crux of what I don't understand. If you could point me to resources that explain what you mean by "Unquoted string expansion != array" and "a bunch of quoting issues", I would appreciate it. – bigdoyle May 04 '18 at 20:41
  • In light of haccks comment below I can now see that quotes don't work. I'm just not understanding the reason. (edit: sorry, I cant figure out how to get the formatting to work) $ LISTA="item1 item2 item3" $ printf '%s\n' ${LIST6[2]]} $ LISTB=(item1 item2 item3) $ printf '%s\n' ${LIST7[2]]} item3 – bigdoyle May 04 '18 at 20:44
  • @charles. I am crawling through the duplicate answers you have linked above. They are helpful, just challenging for the less experienced. – bigdoyle May 04 '18 at 20:53
  • The key thing is that quotes change behavior *on expansion*, not just assignment. So, in `for f in $List`, because the `$List` isn't quoted, the expansion goes through string-splitting (on characters in IFS) and glob-expansion (doing things like replacing `*.txt` with a list of files in the current directory matching that pattern); this is actually one of the behaviors that's responsible for a very large number of the common misunderstandings given in [Bash Pitfalls](http://mywiki.wooledge.org/BashPitfalls). – Charles Duffy May 04 '18 at 21:18
  • ...the same thing is true when expanding an array. If you have `array=( "first item" "second item" )`, then `$array` becomes an equivalent to `"first" "item"` (if `IFS` is at its default value), whereas `"$array"` becomes `"first item"`, whereas `${array[*]}` becomes `"first" "item" "second" "item"`, whereas only `"${array[@]}"` expands to the correct/original/unmodified value of `"first item" "second item"`... keeping in mind that in all of the above the quotes are syntactic, not literal. – Charles Duffy May 04 '18 at 21:20
  • See [What is the difference between "$@" and "$*" in bash?](https://stackoverflow.com/questions/3008695/what-is-the-difference-between-and-in-bash) for an on-point discussion – Charles Duffy May 04 '18 at 21:21

2 Answers2

1

The first two work as I expected by echoing each item in the array, the second two gave unexpected output.

Your fourth loop is equivalent to for f in ${List[0]}; do echo ${f}; done. This is documented in man bash:

Referencing an array variable without a subscript is equivalent to referencing the array with a subscript of 0.

Third loop works but is not printing the original array. Try:

List=(item1 item2 item3)     # Declares array List
printf '%s\n' "${List[@]}"
item1
item2
item3

List="item1 item2 item3"     # Overwrites first element of array... 
                             # ... equivalent to List[0]="item1 item2 item3"
printf '%s\n' "${List[@]}"
item1 item2 item3
item2
item3
builder-7000
  • 7,131
  • 3
  • 19
  • 43
0

First 3 snippets should work, but the problem with 3rd is that List="item1 item2 item3" will replace item1 with "item1 item2 item3" and that's why you are getting the output

item1
item2
item3
item2
item3 

Change the name of the variable and it will work

List2="item1 item2 item3"   

In last snippet $List is used without index and this is same as referring to the content of the first element of the array List.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Ah. I didn't realize that was happening. If I had run #3 first I wouldn't have seen it, or learned it worked that way. Thanks. – bigdoyle May 04 '18 at 20:30