how to split one line with customized separator and assign to variables in BASH?

Viewed 78

First I was using IFS and read, but then I found -- IFS can only support single character as separator and sometimes I need a string to be the separator. Then, I was thinking maybe use awk and eval can do it. So I have a try --

eisen@PVGN34701109A:/tmp> cat file1
hah   a#$hehe#$hoho
eisen@PVGN34701109A:/tmp> cat t2.sh
#!/bin/bash
eval $(awk -F'#\\\$' '{for (i=1;i<=NF;i++)   print "VAR"i"='\''"$i"'\''"}'  file1)
echo "VAR1=  ${VAR1}"
echo "VAR2=  ${VAR2}"
echo "VAR3=  ${VAR3}"
eisen@PVGN34701109A:/tmp> ./t2.sh
awk: warning: escape sequence `\$' treated as plain `$'
VAR1=  hah a
VAR2=  hehe
VAR3=  hoho

At first, I think it's fine. But immediately I found the issue -- in file1, the first value should be "hah a" -- there's 3 space inside, But after awk and eval -- there's only one space left... Then I tested with awk and eval individually, I found it's due to the eval will "eat" some space--

eisen@PVGN34701109A:/tmp> eval "VAR1='hah   a'";echo $VAR1
hah a
eisen@PVGN34701109A:/tmp> eval 'VAR1="hah   a"';echo $VAR1
hah a

I can't find solution myself. Please kind help and if there's any other better way to split one line and assign to variables in BASH, please share your idea. Thanks in advance.

2 Answers

I would suggest using shell arrays for storing individual field values and slightly different awk for this:

IFS=$'\03' read -ra arr < <(awk -F'#\\$' -v OFS='\03' '{$1=$1}1' file)

# check array content
declare -p arr

declare -a arr='([0]="hah   a" [1]="hehe" [2]="hoho")'

We are using control character \03 as output field separator and using same in IFS to make read split fields on \03.


Alternatively you can use sed instead of awk also:

IFS=$'\03' read -ra arr < <(sed 's/#\$/\x03/g' file)

Using a NUL byte with BASH ver 4+

readarray -d $'\0' arr < <(
awk -F'#\\$' -v OFS='\0' '{ORS=OFS; $1=$1} 1' file)
#!/bin/bash

getVar(){ 
    if [ "$1" -eq 0 ]; then  
        awk -F'#\\$' '{for(i=1; i<=NF; i++) print $(i)}' file.txt;
    else
        awk -F'#\\$' -v col="$1" '{printf $(col)}' file.txt;
    fi
} 

# assign variables
VAR1=$(getVar 1)
VAR2=$(getVar 2)
VAR3=$(getVar 3)
# using variables
echo "VAR1 ===${VAR1}==="
echo "VAR2 ===${VAR2}==="
echo "VAR3 ===${VAR3}==="
echo

# declare array
readarray -t my_array < <(getVar 0)
# dump
declare -p my_array|sed 's/declare -a //'
# using variables
echo "VAR1 ===${my_array[0]}==="
echo "VAR2 ===${my_array[1]}==="
echo "VAR3 ===${my_array[2]}==="

output

VAR1 ===hah   a===
VAR2 ===hehe===
VAR3 ===hoho===

my_array=([0]="hah   a" [1]="hehe" [2]="hoho")
VAR1 ===hah   a===
VAR2 ===hehe===
VAR3 ===hoho===
Related