Capture output of a bash command, parse it and store into different bash variables

22,586

Solution 1

Got the answer:

$ read IPETH0 IPLO <<< $(ifconfig | awk '/inet[[:space:]]/ { print $2 }' | cut -d ':' -f 2)
$ echo "${IPETH0}"
192.168.23.2
$ echo "${IPLO}"
127.0.0.1

This assumes the order of the eth0 and lo interfaces, but it shows the basic idea.

Pure awk

You can do this exclusively in awk using the split function:

$ read IPETH0 IPLO <<< $(ifconfig | awk '/inet[[:space:]]/ { split($2,a,":"); print a[2]}')

Solution 2

I presume that you know for each of your commands which part of the command's return you want to store.

In your example this would be words number 7 and 47.

Do it like this (note the back ticks around your command ifconfig):

array=(`ifconfig`)

Show all elements of this array:

echo ${array[@]}
eth0 Link encap:Ethernet HWaddr 30:F7:0D:6D:34:CA inet addr:10.106.145.12 Bcast:10.106.145.255 Mask:255.255.255.0 inet6 addr: fe80::32f7:dff:fe6d:34ca/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1104666 errors:0 dropped:0 overruns:0 frame:0 TX packets:2171 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:444437904 (423.8 MiB) TX bytes:238380 (232.7 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.255.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:15900 errors:0 dropped:0 overruns:0 frame:0 TX packets:15900 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:467306 (456.3 KiB) TX bytes:467306 (456.3 KiB)

Show specific words:

echo ${array[6]} ${array[46]}
addr:10.106.145.12 addr:127.0.0.1

Pipe the result into sed to extract only your IP addresses:

echo ${array[6]} ${array[46]} | sed 's/addr://g'
10.106.145.12 127.0.0.1

Using Arpith's nifty approach with 'read' here is the flexible answer.

read IPETH0 IPLO <<< $(echo ${array[6]} ${array[46]} |\
sed 's/addr://g')
echo $IPETH0 $IPLO
10.106.145.12 127.0.0.1

Please note that array elements are being counted from 0. Therefore your word number 7 would be referred to as "${array[6]}".

The array indexes are positive integers. So you can do all sorts of calculations in your shell script to pick specific words (like ranges or for-loops) ...

To write a portable script you would need to keep a kind of table with these numbers. On my system (BSD) the IP addresses would be numbers 17 and 49 instead of your Linux numbers 7 and 47. Also the resulting strings look different (disregarding my local and Arpith's global ID address):

echo ${array2[16]}
192.168.0.103

echo ${array2[48]}
127.0.0.1

slm's "pure awk" approach (see below) would fail on my BSD system. The split function into an array would not work as my 'ifconfig' command prints "127.0.0.1" and Arpith's "addr:127.0.0.1"...

read IPETH0 IPLO <<< $(ifconfig |\
 awk '/inet[[:space:]]/ { split($2,a,":"); print a[2]}')

HTH

bernie

Solution 3

AWK :

/sbin/ifconfig | awk -F':' 'NR==2{split($2,a," "); print a[1]}'

SED :

ip -f inet addr show dev eth0 | sed -n 's/^ *inet *\([.0-9]*\).*/\1/p'

OR

ifconfig eth0 | sed -n 's/^ *inet addr:*\([.0-9]*\).*/\1/p'

GREP :

ifconfig eth0|grep -Po 't addr:\K[\d.]+'

Thanks to @Stephane Chazelas

Solution 4

A usual way to do that is to have for instance awk generate some bash code and have it interpreted by bash.

Like:

eval "$(
  ifconfig | awk -F '[: ]+' '
    /^[^[:blank:]]/ {iface=$1}
    /inet addr:/ {ip[iface]=$4}
    END {for (i in ip) print "IP" toupper(i) "=" ip[i]}')"
Share:
22,586
Arpith
Author by

Arpith

Senior Software Engineer with MobileIron, Inc. Previously, Software Developer with Cisco Systems, Inc. and Frog Design, Inc.

Updated on September 18, 2022

Comments

  • Arpith
    Arpith over 1 year

    Explanation:

    I have a small bash script which simply runs any Linux command (e.g. say ifconfig)

    The typical output of ifconfig is something like this:

    eth0      Link encap:Ethernet  HWaddr 30:F7:0D:6D:34:CA
              inet addr:10.106.145.12  Bcast:10.106.145.255  Mask:255.255.255.0
              inet6 addr: fe80::32f7:dff:fe6d:34ca/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:1104666 errors:0 dropped:0 overruns:0 frame:0
              TX packets:2171 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000
              RX bytes:444437904 (423.8 MiB)  TX bytes:238380 (232.7 KiB)
    
    lo        Link encap:Local Loopback 
              inet addr:127.0.0.1  Mask:255.255.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:16436  Metric:1
              RX packets:15900 errors:0 dropped:0 overruns:0 frame:0
              TX packets:15900 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0
              RX bytes:467306 (456.3 KiB)  TX bytes:467306 (456.3 KiB)
    

    Now what most people usually do is store the entire output into a file/variable and parse based on that. I however want to know if there is anyway that I could put specific parts of the output in more than one variable (say a bash variable called ${IPETH0} to carry the IP address 10.106.145.12 from eth0 and ${IPLO} to carry the IP address 127.0.0.1 from lo in the above example without running ifconfig command twice).

    Something like what tee command does with the input but I want to do this for the output and store the output into 2 or more variables in one go. Any ideas?

    • slm
      slm over 10 years
    • Arpith
      Arpith over 10 years
      @slm: I am not trying to obtain the IP address. I wanna know how I can store broken-down output of a command into more than one bash variable. The example you pointed out still stores output in a single variable.
    • slm
      slm over 10 years
      @Arpith - yeah that was just to get you started, not show you exactly your solution.
    • Bananguin
      Bananguin about 6 years
    • terdon
      terdon about 6 years
      @Bananguin true, but this is 4 and a half years old. Not much point in doing anything about it anymore, I'm afraid.
    • Bananguin
      Bananguin about 6 years
      @terdon I suppose. But maybe others get the hint.
  • jordanm
    jordanm over 10 years
    You shouldn't use awk and cut together. You only need awk.
  • Arpith
    Arpith over 10 years
    I know. I know very minimal awk. Hence the cacophony. :-p
  • slm
    slm over 10 years
    @jordanm - i added a awk only method.