Reading a space-delimited string into an array in Bash

292,242

Solution 1

In order to convert a string into an array, create an array from the string, letting the string get split naturally according to the IFS (Internal Field Separator) variable, which is the space char by default:

arr=($line)

or pass the string to the stdin of the read command using the herestring (<<<) operator:

read -a arr <<< "$line"

For the first example, it is crucial not to use quotes around $line since that is what allows the string to get split into multiple elements.

See also: https://github.com/koalaman/shellcheck/wiki/SC2206

Solution 2

In: arr=( $line ). The "split" comes associated with "glob".
Wildcards (*,? and []) will be expanded to matching filenames.

The correct solution is only slightly more complex:

IFS=' ' read -a arr <<< "$line"

No globbing problem; the split character is set in $IFS, variables quoted.

Solution 3

Try this:

arr=(`echo ${line}`);

Solution 4

If you need parameter expansion, then try:

eval "arr=($line)"

For example, take the following code.

line='a b "c d" "*" *'
eval "arr=($line)"
for s in "${arr[@]}"; do 
    echo "$s"
done

If the current directory contained the files a.txt, b.txt and c.txt, then executing the code would produce the following output.

a
b
c d
*
a.txt
b.txt
c.txt
Share:
292,242

Related videos on Youtube

Nikola Novak
Author by

Nikola Novak

Updated on July 08, 2022

Comments

  • Nikola Novak
    Nikola Novak almost 2 years

    I have a variable which contains a space-delimited string:

    line="1 1.50 string"
    

    I want to split that string with space as a delimiter and store the result in an array, so that the following:

    echo ${arr[0]}
    echo ${arr[1]}
    echo ${arr[2]}
    

    outputs

    1
    1.50
    string
    

    Somewhere I found a solution which doesn't work:

    arr=$(echo ${line})
    

    If I run the echo statements above after this, I get:

    1 1.50 string
    [empty line]
    [empty line]
    

    I also tried

    IFS=" "
    arr=$(echo ${line})
    

    with the same result. Can someone help, please?

  • Banjer
    Banjer over 10 years
    and to do a sanity check of your beautiful new array: for i in ${arr[@]}; do echo $i; done
  • Banjer
    Banjer over 10 years
    or just echo ${arr[@]}
  • Keith Hughitt
    Keith Hughitt over 9 years
    Nice -- This solution also works in Z shell where some of the other approaches above fail.
  • Tino
    Tino over 9 years
    Both ways may fail if $line has globbing characters in it. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]} gives 3
  • Zorf
    Zorf about 9 years
    The looping is wrong, it splits the array fine and the pipe into tr is superfluous but it should loop over "${arr[@]}" instead, not $arr
  • Dave
    Dave over 8 years
    declare -a "arr=($line)" will ignore IFS delimiters inside quoted strings
  • georg
    georg about 8 years
    Both solutions work for me on the cmd-line. arr=($line) doesn't work for me within a bash script whereas read -a arr <<<$line does.
  • Awknewbie
    Awknewbie almost 8 years
    What need to be done if the file was delimited eg :"1,1.50,string" . Can delimite be specified before feeding to array.
  • kev
    kev almost 8 years
    @Awknewbie try this command: arr=($(echo '1,1.50,string' | tr ',' ' '))
  • vr3C
    vr3C over 7 years
    Its does the work, could you please explain why it works?
  • Manuel Rozier
    Manuel Rozier over 7 years
    Works also with ksh. Beware that declaring arr=("a b") won't work, you need to explicitly declare the intermediary variable "line". line="a b" then arr=($line)
  • Tim Bird
    Tim Bird over 7 years
    You can avoid wildcard expansion with: set -f && arr=($line) && set +f
  • Johnny Wong
    Johnny Wong over 6 years
    @Tino No. When line='*', read -a arr <<<$line always work, but only arr=($line) fails.
  • Tino
    Tino over 6 years
    @JohnnyWong Good find! Sorry that I mixed that with ksh where a='*'; read -A b <<<$a fails while a='*'; read -A b <<<"$a" works. Note that bash uses read -a while ksh uses read -A.
  • Johnny Wong
    Johnny Wong over 6 years
    Remark: this doesn't work either when the line have '*' in it, like line='*'
  • codeforester
    codeforester about 6 years
    This should be the accepted answer. The statement arr=($line) in the accepted answer suffers from globbing issues. For example, try: line="twinkling *"; arr=($line); declare -p arr.
  • codeforester
    codeforester about 6 years
    Quoting is optional for herestring, <<< but it may be a good idea to still use double quotes for consistency and readability.
  • artu-hnrq
    artu-hnrq about 3 years
    Looking for a GNU Make 4.2.1 solution, but is not that
  • artu-hnrq
    artu-hnrq about 3 years
    Looking for a GNU Make 4.2.1 solution, but it doesn't did the job
  • done
    done about 3 years
    @artu-hnrq Make use the sh shell. That shell has no arrays. The question is about arrays. Can't give you an answer compatible with both requirements. Unless you claim that the positional arguments are the only array in sh and, then, this: set -- $line; printf '%s\n' "$@" would work. Note that glob characters are still a problem in this case.
  • artu-hnrq
    artu-hnrq about 3 years
    You are right @Isaac! I made my own question
  • TonyH
    TonyH about 3 years
    Shellcheck SC2207 argues against arr=($line). I like how concise this is, but maybe it's not worth using?
  • elulcao
    elulcao almost 3 years
    This solution worked for me, thanks a lot.
  • Gabriel Staples
    Gabriel Staples about 2 years
  • Togomi
    Togomi almost 2 years
    Very simple solution for CentOS8