2

I fail to understand what is happening between these two array creation, tested on cygwin GNU bash

$ array=('hello world' how are you)
$ echo $array
'hello world'    <----- This is expected   

EDIT : As chepner pointed it out the output is

hello world      <----- no ''

Now with variable assignment as an intermediate step

$ words="'hello world' how are you"
$ echo $words
'hello world' how are you
$ array=($words)
$ echo $array
'hello           <----- Why was it split with ' '? 
2
  • 2
    As an aside: the line marked with <----- This is expected is not actually what you get: The single quotes have syntactic function and are therefore removed by the shell, so the actual output is: hello world (no enclosing single quotes).
    – mklement0
    Commented Jun 1, 2017 at 20:08
  • 3
    Use declare -p array to get a better idea of what is actually being created in each case.
    – chepner
    Commented Jun 1, 2017 at 20:11

6 Answers 6

3

codeforester's helpful answer explains the issue well.

As for a solution:

Note: If the input you're dealing with doesn't actually have embedded quoting to demarcate elements, simply use readarray -t array < <(...) as is, where (...) represents the output-producing command whose lines should be captured as the individual elements of an array.

xargs generally understands shell quoting in literal input (except for escaped, embedded quotes):

words="'hello world' how are you"
echo "$words" | xargs -n 1 printf '%s\n'
hello world
how
are
you

Note how hello world was recognized as a single argument, and how its enclosing ' instances were removed.

To utilize this when creating an array in bash (assumes Bash 4.x due to readarray, but it's possible to make it work in Bash 3.x[1] ):

words="'hello world' how are you"
readarray -t array < <(xargs -n 1 printf '%s\n' <<<"$words")
declare -p array

which yields:

declare -a array=([0]="hello world" [1]="how" [2]="are" [3]="you")

(The enclosing " instances are an artifact of the declare -p output format - they have syntactic function, if you were to reuse the output as shell source code.)


[1] A Bash v3+ solution, using read instead of readarray:
words="'hello world' how are you" IFS=$'\n' read -d '' -ra array < <(xargs -n 1 printf '%s\n' <<<"$words") declare -p array

2
  • This is great if you statically assign words="'hello world' how are you", but what if you're trying to do this with results of md5sum and filenames with spaces where the output would be: 2345asf32asdf some file name.txt Commented Nov 26, 2019 at 2:51
  • 1
    @spezticle: Please ask a new question.
    – mklement0
    Commented Nov 26, 2019 at 2:57
3

From Bash manual:

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

When you put single quotes inside double quotes as in:

words="'hello world' how are you"

the single quotes become a literal part of the string and hence won't prevent word split when you initialize the array with

array=($words)

wherein 0th element of the array would become 'hello. To prevent word splitting in this case, do this:

array=("$words")

However, that wouldn't achieve your intention of making hello world as the 0th element. The direct assignment array=('hello world' how are you) is the right way of doing it.


See also:

1
  • I see. I wanted to make an array with the output of a command array=$(command), but this problem arise. With the insight you gave me, i should be able to achieve this by assigning IFS=$'\n' in order to ignore ' '
    – BragiPls
    Commented Jun 1, 2017 at 20:01
0

It's the dereferencing. Try this:

$ array=('hello world' how are you)
$ echo $array
hello world
$ echo ${array[@]}
hello world how are you
$ echo ${array[0]}
hello world
$ echo ${array[1]}
how
$ echo ${array[2]}
are
$ echo ${array[3]}
you
1
  • I think my question was not clear, sorry. My desire is to understand why it's not the same array that's created when using a variable containing the string
    – BragiPls
    Commented Jun 1, 2017 at 19:37
0

"Referring to the content of a member variable of an array without providing an index number is the same as referring to the content of the first element, the one referenced with index number zero." From this page, last line of section 10.2.2

So

$ echo $array

is equivalent to

$ echo ${array[0]}
0

In the first case single quotes prevent word splitting to split hello world in two arguments, the single quotes are not echoed

$ array=('hello world' how are you)
$ echo $array
hello world

in the second, the quotes are string literal, arguments are split, this can't be seen because echo displays all arguments, if you use printf "<%s>\n" $words you will see better

$ words="'hello world' how are you"
$ echo $words
'hello world' how are you
$ array=($words)
$ echo $array
'hello         

$ words="'hello world' how are you"
$ printf "<%s>\n" $words
<'hello>
<world'>
<how>
<are>
<you>

As said in comments, eval is dangerous, you must know what you are doing

$ eval "array=($words)"
$ printf "<%s>\n" "${array[@]}"
<hello world>
<how>
<are>
<you>
3
  • Oh, so when assigning an array, quotes are special instructions ! I thought it was the character itself
    – BragiPls
    Commented Jun 1, 2017 at 19:46
  • it's not specific to array, it's the same for command parsing Commented Jun 1, 2017 at 19:49
  • command line parsing has many steps and after variable expansion special character generally loses there meaning to avoid injection, but the parsing can be done a more time with the eval builtin but this is why eval is called evil you have to know what you're doing. Commented Jun 1, 2017 at 19:57
0

What about something like

$ array=($(echo -e $'a a\nb b' | tr ' ' '§')) && array=("${array[@]//§/ }") && echo "${array[@]/%/ INTERELEMENT}"

a a INTERELEMENT b b INTERELEMENT

Not the answer you're looking for? Browse other questions tagged or ask your own question.