maaboo
maaboo

Reputation: 137

Read multi-line delimited sub-strings to bash array

I'd like to create array containing

Speed: 10500 Mbps
Size: 122400 MB
Load: 4544 Kg
Distance: 232680 miles
Acceleration: 11200 meters/s2
Deviation: 1100 Wierdos

From the following code:

read -r -d '' TEST_STRING << EOM
Speed: 10500 Mbps; Size: 122400 MB
Load: 4544 Kg; Distance: 232680 miles
Acceleration: 11200 meters/s2; Deviation: 1100 Wierdos
EOM

STRINGS_ARRAY=()
RE_INTRALINEDELIMITER=";"

while IFS=$'\n' read -a LINE; do

    if [[ $LINE =~ $RE_INTRALINEDELIMITER ]]; then      
        echo "(intraline): $LINE"
        while IFS=$';' read -a SUBSTR; do
            echo "(substr): $SUBSTR"
        done <<< "$LINE"

    fi

done <<< "$TEST_STRING"

(echoes are added for debugging and as an empty operator). Then process lines with some functions and finally to assembly it back to the original string.

But for SUBSTR I get only first sub-string from every string (before semicolon). What I'm doing wrong?

Upvotes: 2

Views: 855

Answers (3)

chepner
chepner

Reputation: 530920

Use read twice; once get a line, and again to split that line.

test_string='Speed: 10500 Mbps; Size: 122400 MB
Load: 4544 Kg; Distance: 232680 miles
Acceleration: 11200 meters/s2; Deviation: 1100 Wierdos'

while IFS= read -r line; do
    IFS=";" read -r fields <<< "$line"
    strings_array+=("${fields[@]}")
done <<< "$test_string"

Upvotes: 0

Inian
Inian

Reputation: 85530

With read -ra you are basically reading into an array SUBSTR but only trying to print it in a string variable's context. Try printing the whole array, which should have the complete line stored.

echo "(substr): ${SUBSTR[@]}"

Also mind the -a usage earlier in the code for LINE where you have the entire line stored as an array.

Also if you have pretty recent versions of bash installed, try using mapfile/readarray to parse multi-line output into an array. Your entire requirement could very well be reduced to

re_delimiter=';'
mapfile -t stringArray <<<"$TEST_STRING"

This stores the entire string in the array, from which you loop over one entry at a time to see if the line has a ; character present. I have initialized wholeArray here to store all the ; delimited strings from all the lines in your input string.

wholeArray=()
for line in "${stringArray[@]}"; do
    substringArray=()
    if [[ $line =~ $re_delimiter ]]; then
        IFS=';' read -ra substringArray <<<"$line"
        wholeArray+=( "${substringArray[@]}" )
    fi
done

and now print the entire array content as

declare -p wholeArray
declare -a wholeArray='([0]="Speed: 10500 Mbps" [1]=" Size: 122400 MB" [2]="Load: 4544 Kg" [3]=" Distance: 232680 miles" [4]="Acceleration: 11200 meters/s2" [5]=" Deviation: 1100 Wierdos")'

(or) print the element one at a time using a proper for-loop

for entry in "${wholeArray[@]}"; do
    printf '%s\n' "$entry"
done

Also always use upper-case variables to store variable names to not confuse with environment variables maintained by the shell.

Upvotes: 2

Nahuel Fouilleul
Nahuel Fouilleul

Reputation: 19305

Another way to split strings, using shell expansions and avoiding read which is slow :

tmp=$TEST_STRING

while [[ $tmp = *[\;$'\n']* ]]; do
    item=${tmp%%[;$'\n']*}   # to remove the largest suffix starting with ; or newline
    tmp=${tmp#*[;$'\n']}     # to remove the shortest prefix ending with ; or new line
    echo "[$item]"
done

Upvotes: 0

Related Questions