Mike D
Mike D

Reputation: 365

Bash - assign variables to output of a command in a loop

I've got an application that runs and defines the devices on a system. The output looks something like this:

./show-devices 192.168.1.2 | grep volume

/dev/sda        volumeid-65
/dev/sdb        volumeid-81
/dev/sdc        volumeid-92

What I'm trying to do is run the application in a loop and assign variables like so:

sda=volumeid-65
sdb=volumeid-81
sdc=volumeid-92

Then, be able to use the $sda, $sdb, and $sdc variables later on in the script. Not all outputs are the same. Some machines will only have one volume, some will have more. All suggestions are welcome. I've tried many combinations of a for loop and don't appear to be getting anywhere. Thank you!

Upvotes: 0

Views: 3089

Answers (2)

Samveen
Samveen

Reputation: 3540

How about:

eval `./show-devices 192.168.1.2 | grep volume | while read d v ;do
    echo $(basename $d)=$v
done`

However, you'll not be sure of the names of the variables. So it's better to use an associative array:

# declare the array
declare -A volumes

# Assign values to the array
eval `./show-devices 192.168.1.2 | grep volume | while read d v ;do
    echo volumes[$(basename $d)]=$v
done`

# Use the array
for i in "${!volumes[@]}"; do
    echo "Key:$i , Value:${volumes[$i]}"
    # Declare all the variables with the values as asked by question
    eval $i=${volumes[$i]}
done

Explaination: | while read d v loops over the output of the previous command reading 2 values per line. basename $d gives me the base name of the device and $v contains the volume associated with the device. This is assigned into the volumes associative array declared with declare -A volumes. The while loop runs ins a sub-shell and prints volumes[sda]=... and eval evaluates this in the current shell, actually assigning the value.

Usage: The for loop is a usage example. "${!volumes[@]}" is the list of keys for the volumes array, and the for loop loops over them and prints the value associated with each key in the volumes array.

Edits:

  • Modified the while loop to echo the assigment and eval the whole thing (to get around the subshell being spawned by the pipe.

  • Added usage info

  • Added exact solution asked in question

Upvotes: 2

glenn jackman
glenn jackman

Reputation: 247052

source <(
    ./show-devices 192.168.1.2 |
    sed -rn '/volume/{s#/dev/([^[:space:]]+)[[:space:]]+#\1=#;p}'
)

This runs your command, passes it through sed to format it like shell assignments, and then uses a process substitution to treat the output like a file which is sourced into the current shell.

Upvotes: 1

Related Questions