Home > Mobile >  How to read a line into variables, which is in 'var=value' format, in a bash script?
How to read a line into variables, which is in 'var=value' format, in a bash script?

Time:06-01

I'm trying to read a line from output, which looks like this (it comes from slurm, for those who are familiar with it):

cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K

After reading the line, there should be variables cpu, energy, a.s.o. with the respective value.

Initially I tried to source the output via piping:

line=$(tr ',/' ';_' <<< "cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K)"
source <<< $line
. <<< $line

But that doesn't work since source and . needs a file. So my working attempt now is:

file=$(mktemp)
{ sacct [...] | tr ',/' ';_' > $file && source $file && rm $file } || echo "Error"

My question would be: is there a better way to achieve the same result without creating a temporary file?

CodePudding user response:

Another way that avoids the unsafe eval:

#!/usr/bin/env bash

line="cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K"

# Turn the / into an underscore since / can't be in an identifier
# Then read the line into an array splitting on commas
IFS=, read -ra vars <<<"${line//\//_}"
# Define all the variables
declare -- "${vars[@]}"
# And display them
declare -p cpu energy fs_disk mem pages vmem

prints out

declare -- cpu="00:00:00"
declare -- energy="0"
declare -- fs_disk="1389.75K"
declare -- mem="556K"
declare -- pages="0"
declare -- vmem="203640K"

CodePudding user response:

If using bash version at laest 4.2, it is safer to parse it into an associative array that can store arbitrary key strings. Otherwise, some identifiers will fail dramatically as invalid Bash variable identifiers like fs/disk which cannot be used as a Bash variable name:

#!/usr/bin/env bash

line='cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K'

# Read the line into a regular array, splitting keys and values at , and = signs
IFS=,= read -r -a kv <<<"$line"

# Generates an Associative array elements delcarations
# by print quoting [key]=value pairs
# shellcheck disable=SC2155 # Safely generated declaration
declare -A map="($(
  printf '[%q]=%q ' "${kv[@]}"
))"

# Print out a nice output for demo purpose:
for k in "${!map[@]}"; do
  printf '%-8s %s\n' "$k" "${map[$k]}"
done

Output:

fs/disk  1389.75K
vmem     203640K
cpu      00:00:00
pages    0
energy   0
mem      556K

Alternate method to populate the Associative array from the line, using a loop:

declare -A map=()
while IFS='=' read -r -d, k v; do
  map["$k"]="$v"
done <<<"$line"

CodePudding user response:

try

eval `echo 'cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K' | tr ',/' ';_'`
  •  Tags:  
  • bash
  • Related