BragiPls
BragiPls

Reputation: 17

Creating a Bash array with elements that have whitespaces in them

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 ' '? 

Upvotes: 2

Views: 6690

Answers (6)

Whimusical
Whimusical

Reputation: 6649

What about something like

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

a a INTERELEMENT b b INTERELEMENT

Upvotes: 0

mklement0
mklement0

Reputation: 440337

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

Upvotes: 3

Nahuel Fouilleul
Nahuel Fouilleul

Reputation: 19335

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>

Upvotes: 0

codeforester
codeforester

Reputation: 43089

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:

Upvotes: 3

CAB
CAB

Reputation: 1148

"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]}

Upvotes: 0

Jack
Jack

Reputation: 6198

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

Upvotes: 0

Related Questions