15

I have a string

echo $STRING

which gives

first second third fourth fifth

basically a list separated spaces.

how do i take that string and make it an array so that

array[0] = first
array[1] = second

etc..

I have tried

IFS=' ' read -a list <<< $STRING

but then when i do an

echo ${list[@]}

it only prints out "first" and nothing else

2

2 Answers 2

27

It's simple actually:

list=( $STRING )

Or more verbosely:

declare -a list=( $STRING )

PS: You can't export IFS and use the new value in the same command. You have to declare it first, then use its effects in the following command:

$ list=( first second third )
$ IFS=":" echo "${list[*]}"
first second third
$ IFS=":" ; echo "${list[*]}"
first:second:third

Notice that the last example will change IFS to ":" until you change it again, or the shell exits. Usually you want to use a subshell, or save the original value of IFS so you can restore it afterwards.

Sign up to request clarification or add additional context in comments.

3 Comments

awesome that worked. But how does it know what to split it by?
IFS' default value is ` \t\n\r` or something like that. When assigning to an array using the ()-syntax everything between the parentheses gets expanded like parameters of a command, turning every "parameter" to an element of the array.
list=( $STRING ) is an antipattern; see BashPitfalls #50
0

Your original code generally works -- the quoting is a bit off (and how much that matters is version-specific: you wouldn't see your original bug on a newer version of bash), but nothing further.

After:

# assign string
string='this is several words'

# split string to a list
IFS=' ' read -r -a list <<<"$string"

...you can run:

# print a definition of the variable list
$ declare -p list
declare -a list=([0]="this" [1]="is" [2]="several" [3]="words")

# print each item in the list array on a separate line
$ printf ' - %s\n' "${list[@]}"
- this
- is
- several
- words

# still works with echo; change IFS to show that we're really an array
$ IFS=':' echo "${list[*]}"
this:is:several:words

More importantly, it also works in cases where the competing answer doesn't:

# need to be in a non-empty directory to demonstrate
$ touch a.txt b.txt c.txt

# put literal asterisks as separate words in our string
$ string='list * contains * asterisks'

# reading those using read -a works fine
$ IFS=' ' read -r -a list <<<"$string"

# whereas the array=( $string ) is NOT fine
$ IFS=' '; buggyList=( $string )

# print both arrays' contents
$ declare -p list buggyList
declare -a list=([0]="list" [1]="*" [2]="contains" [3]="*" [4]="asterisks")
declare -a buggyList=([0]="list" [1]="a.txt" [2]="b.txt" [3]="c.txt" [4]="contains" [5]="a.txt" [6]="b.txt" [7]="c.txt" [8]="asterisks")

As you can see, the list=( $string ) answer gets filenames injected into its contents, but the read -a list contains exactly the word-split contents of your original string.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.