Tyson
Tyson

Reputation: 153

Reading delimited output into multiple Bash variables

This should be simple and I've done it in a script somewhere before, I can't find my example (or an equivalent), and today this problem is driving me toward insanity. (Even tho I've not included the rest of script this is for use inside a script, not interactive.)

cat testfile | grep -e eth0

Returns:

eth0         123.45.67.8/23                  u/u  Internet - Cable WAN

The end result is I need variables set for each element. i.e. as if I had done this manually instead:

INTF = "etho"
IPADDR = "123.45.67.8/23"
STS = "u/u"
DESC = "Internet - Cable WAN"

I thought I could do something like:

cat testfile | grep -e eth0 | awk '{print $2}' | xargs read IPADDR

or

cat testfile | grep -e eth0 | cut -d " " -n2 | read IPADDR

but nothing I've tried has brought joy.... What is my roadblock (headblock)?

EDIT to add— the script is more complicated than just grabbing one IP, as my example is leading people to conclude. It’s a cron based script that runs once per minute, it runs a loop thru 8 interfaces and sends a message in certain alarm conditions. The rest of the script works when I run it with hard coded variables, I just cut asked about the part that is stumping me.

Upvotes: 5

Views: 900

Answers (4)

codeforester
codeforester

Reputation: 43079

In your commands

cat testfile | grep -e eth0 | cut -d " " -n2 | read IPADDR
cat testfile | grep -e eth0 | awk '{print $2}' | xargs read IPADDR

the read command runs in a subshell and it won't be visible to the main shell.

You need process substitution to get the values into the parent shell in one go:

read -r intf ipaddr sts desc < <(grep eth0 testfile)

The first three variables would get the value of the first three fields in grep's output (which is string split based on IFS) and the fourth variable desc will get the remaining tokens in the output.


As an aside, cat outfile | grep -e eth0 is a case of UUOC. Just grep eth0 outfile would do your job.


Related:

Upvotes: 0

Mike Q
Mike Q

Reputation: 7337

Tons of ways to do this, You could even scan over your test file and put the results into arrays one to one matching:

#!/bin/bash
file="testfile.txt"

declare -a intf
declare -a ipaddr
declare -a sts
declare -a desc

# - file reader -
while IFS= read -r line; do
    if [[ ${line,,} == *"intf"* ]];then
        intf+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
    elif [[ ${line,,} == *"ipaddr"* ]];then
        ipaddr+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
    elif [[ ${line,,} == *"sts"* ]];then
        sts+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
    elif [[ ${line,,} == *"desc"* ]];then
        desc+=("$(echo $line | cut -d'"' -f2- | cut -d'"' -f1)")
    fi
done < "$file"

if [[ ${#intf[@]} -eq  ${#ipaddr[@]} &&  ${#intf[@]} -eq ${#ipaddr[@]} &&  ${#intf[@]} -eq  ${#sts[@]} ]] ;then
    echo "file read successful! continuing .."
else
    echo "icky, this didn't work"
fi

for ((i=0; i< ${#intf[@]}; i++)) ;do
       echo -e "INTF=${intf[$i]} \nIPADDR=${ipaddr[$i]} \n"

done

output (something like):

$ ./script.sh
file read successful! continuing ..
INTF=etho 
IPADDR=123.45.67.8/23 

INTF=etho 
IPADDR=13.45.67.8/23 

INTF=etho 
IPADDR=23.45.67.8/23 

Upvotes: 0

Nic3500
Nic3500

Reputation: 8621

Since you want to set 4 variables, instead of doing cut 4 times, you can use read like this:

#!/bin/bash
#
read INTF IPADDR STS DESC <<< $(cat testfile | grep -e eth0)

echo $INTF
echo $IPADDR
echo $STS
echo $DESC

This will "cut" on any white space, using the default $IFS.

If you wanted to cut values from: "aaa,bbb,ccc,ddd",
you can change the IFS value before the read.

Ex:

IFS="," read INTF IPADDR STS DESC <<< $(cat testfile | grep -e eth0)

Upvotes: 4

If you want to get all the variables assigned at once using read, you can do it as follows:

read INTF IPADDR STS DESC <<< `cat testfile | grep -e eth0`

Upvotes: 2

Related Questions