5

I already read How to split a string into an array in bash but the question seems a little different to me so I'll ask using my data.

I have this line comming from STDIN :

(5,[a,b,c,d,e,f,g,h,i,j])

The five is my group ID and the letters are values of an array (the group data). I need to get the group ID into a var and the letters into something I can work using IFS=',' read -r -a array <<< "$tline"

8
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?.

  • As @J. Starnes answer yours seems very complext to me although it seems very professional. This complexity will prevent me to adapt the code to future needs and I'll come here again to ask similar questions. So I'll accept the Kusalananda's answer because of its simplicity. I don't like to just copy/paste code I don't know what are doing. – Magno C Oct 3 '17 at 11:20
  • I promise I'll try to understand you code later. As programmers we need to evolve and learn new things. Giving it a close look I think it's not a very ugly monster. Thanks ! – Magno C Oct 3 '17 at 11:30
  • 1
    Jesus! Its enough... crystal clear now!! Many thanks. – Magno C Oct 3 '17 at 17:35
  • Tell me a little more about back-slash... I may have one in my inputs...is it a problem? – Magno C Oct 3 '17 at 17:39
3
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
<
2

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.

0

Given the formating of your data read would not put it into an array cleanly. sed can be used to "clean" the data.

declare -a TLINEARRAY
TLINEARRAY=($(sed -e 's/,/ /g' -e 's/\[/ /g' -e 's/\]//g' <<< "$tline" ))
echo ${TLINEARRAY[*]}

Using read as you tried.

read -a TLINEARRAY <<< "$(sed -e 's/,/ /g' -e 's/\[/ /g' -e 's/\]//g' <<< "$tline" )"
echo ${TLINEARRAY[*]}

Both methods output an array containing 5 a b c d e f g h i j.

0

We can strip the punctuation characters by using sed tool, hence you will get only numbers and letters out of that line:

a="(5,[a,b,c,d,e,f,g,h,i,j])"

echo $a | sed 's/[[:punct:]]/ /g'

Output:

5  a b c d e f g h i j 
  • Welcome to the site, and thank you for your contribution. Please note, however, that the OP is looking for a way to read the number into a scalar shell variable and the individual letters into an array variable, not merely replace the field separator characters with a space. – AdminBee Jul 6 '20 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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