Reputation: 11
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?
Upvotes: 1
Views: 61
Reputation: 19555
If using bash version at laest 4.0, 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"
Upvotes: 1
Reputation: 52354
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"
Upvotes: 2
Reputation: 564
try
eval `echo 'cpu=00:00:00,energy=0,fs/disk=1389.75K,mem=556K,pages=0,vmem=203640K' | tr ',/' ';_'`
Upvotes: -1