How to convert a String into Array in shell script

bkpIFS="$IFS"

IFS=',()][' read -r -a array <<<"(5,[a,b,c,d,e,f,g,h,i,j])"
echo ${array[@]}    ##Or printf "%s\n" ${array[@]}
5 a b c d e f g h i j

IFS="$bkpIFS"

Explanations:

  • First we are taking backup of default/current shell IFS with bkpIFS="$IFS";
  • Then we set IFS to set of delimiters ,, (, ), ] and [ with IFS=',()][' which means our input string can be delimited with one-or-more of these delimiters.

  • Next read -r -a array reads and split the line into an array called array only based on defined IFS above from input string passed in Here-String method. The -r option is used to tell read command don't does expansion on back-slash \ if come in input.

    IFS=',()][' read -a array <<<"(5,[a,b,c,d,e,f,g,h,i,j,\,k])"
    echo ${array[@]}
    5 a b c d e f g h i j ,k
    

    see the last ,k which it caused by having back-slash in input and read without its -r option.

  • With echo ${array[@]} we are printing all elements of array. see What is the difference between $* and $@? and Gilles's answer about ${array[@]} there with more details.

  • With printf "%s\n" ${array[@]} also there is other approach to printing array elements.

  • Now you can print a specific element of array with printf "%s\n" ${array[INDEX]} or same with echo ${array[INDEX]}.

  • Ah, sorry, forgot to give IFS back to shell, IFS="$bkpIFS" : )

Or using awk and its split function.

awk '{split($0,arr,/[][,)(]/)} 
    END{for (x in arr) printf ("%s ",arr[x]);printf "\n"}' <<<"(5,[a,b,c,d,e,f,g,h,i,j])"

Explanations:

  • Same here, we are splitting the entire line of input based on defined group of delimiters [...] in regexp constant /[...]/ which support in modern implementation of awk using split function. read more in section of split() function.

  • Next at the END{for (x in arr) printf ("%s ",arr[x]); ...} we are looping over array called arr and print their corresponding value. x here point to the index of array arr elements. read more about awk's BEGIN/END rules.

Side-redirect to How to add/remove an element to the array in bash?.


data=$(tr -d '[]()' | tr ',' '\n')

readarray -t -n 1 group   <<<"$data"
readarray -t -s 1 letters <<<"$data"

printf 'group = %s\n' "$group"
printf 'data: %s\n' "${letters[@]}"

This will first get rid of all () and [] from the input data that is arriving on standard input using tr, and then it will replace the commas with newlines and assign the result to data.

We then use readarray to parse this data.

The first call will only read the first entry (with -n 1) and assign it to the variable group.

The second call to readarray will skip the first entry (with -s 1) and assign the remaining entries to the array letters.

The -t removes the actual newlines from each entry.

Even though group is an array here, it's only containing one single element, and you may use it as $group.

$ echo '(5,[a,b,c,d,e,f,g,h,i,j])' | bash ./script.sh
group = 5
data: a
data: b
data: c
data: d
data: e
data: f
data: g
data: h
data: i
data: j

The following retains the commas in the string and lets readline use these to delimit the entries, but for some reason, the last element of letters has a newline at the end:

data=$(tr -d '[]()')
readarray -d, -t -s 1 letters <<<"$data"

printf '>%s<\n' "${letters[@]}"

Running:

$ echo '(5,[a,b,c,d,e,f,g,h,i,j])' | bash ./script.sh
>a<
>b<
>c<
>d<
>e<
>f<
>g<
>h<
>i<
>j
<

POSIXly:

string='(5,[a,b,c,d,e,f,g,h,i,j])'
set -o noglob
IFS=',['
string=${string#'('}
string=${string%'])'}
set -- $string''
gid=$1; shift 2
printf '%s\n' "gid=$gid; group-data:"
printf '   <%s>\n' "$@"

It should work with any value for the group-data fields, even those with newline characters.