Reputation: 1308
I'm trying to use bash's variable name expansion, but I can't seem to get this to work. ${!${prefix}*}
in particular is what is failing. Below is a reproducible example.
#!/bin/bash
MountVolumes_b_mkfs_options='foo bar baz'
MountVolumes_b_path=/foo/bar/baz
MountVolumes_b_mnt_options='foo bar baz'
MountVolumes_b_fs=xfs
MountVolumes_c_path=/foo/bar/baz
MountVolumes_c_fs=xfs
MountVolumes_c_mkfs_fs_options=-'t really -foo /ugly/options'
MountVolumes_c_mkfs_options='-t really -foo /ugly/options'
prefixes=($(echo "${!MountVolumes*}" | grep 'MountVolumes_[b-z]' -o | uniq))
for prefix in ${prefixes[@]}; do
echo "prefix: ${prefix}"
##I need this to expand to:
##MountVolumes_b_mkfs_options MountVolumes_b_path MountVolumes_b_mnt_options MountVolumes_b_fs
echo "${!${prefix}*}"
done
echo "${!MountVolumes_b*}" ##Works
How do I do this?
Upvotes: 0
Views: 120
Reputation: 1308
I accepted the answer as it solves the question, but I ended up doing it a slightly different way as has pretty severe security implications with script injection.
Posting it here in case it helps anyone.
#!/bin/bash
MountVolumes_b_mkfs_options='foo bar baz'
MountVolumes_b_path=/foo/bar/baz
MountVolumes_b_mnt_options='foo bar baz'
MountVolumes_b_fs=xfs
MountVolumes_c_path=/foo/bar/baz
MountVolumes_c_fs=xfs
MountVolumes_c_mkfs_fs_options=-'t really -foo /ugly/options'
MountVolumes_c_mkfs_options='-t really -foo /ugly/options'
build_opt() {
key="${1}_${2}"
build_opt_res=" --${2} '${!key}'"
}
prefixes=($(echo "${!MountVolumes*}" | grep 'MountVolumes_[b-z]' -o | uniq))
for prefix in ${prefixes[@]}; do
build_opt "$prefix" "mkfs_options"
echo "${build_opt_res}"
build_opt "$prefix" "path"
echo "${build_opt_res}"
done
Stripping out single quotes from ${!key} in build_opt() and hardcoding the known suffixes should remove the possibility of script injection.
Upvotes: 0
Reputation: 26447
This can achieve what's needed :
#!/bin/bash
MountVolumes_b_mkfs_options='foo bar baz'
MountVolumes_b_path=/foo/bar/baz
MountVolumes_b_mnt_options='foo bar baz'
MountVolumes_b_fs=xfs
MountVolumes_c_path=/foo/bar/baz
MountVolumes_c_fs=xfs
MountVolumes_c_mkfs_fs_options=-'t really -foo /ugly/options'
MountVolumes_c_mkfs_options='-t really -foo /ugly/options'
prefixes=($(echo "${!MountVolumes*}" | grep 'MountVolumes_[b-z]' -o | uniq))
for prefix in ${prefixes[@]}; do
echo "prefix: ${prefix}"
##I need this to expand to:
##MountVolumes_b_mkfs_options MountVolumes_b_path MountVolumes_b_mnt_options MountVolumes_b_fs
declare -a "vars=(\${!${prefix}*})"
echo "${vars[@]}"
done
echo "${!MountVolumes_b*}" ##Works
Upvotes: 2
Reputation: 246754
You'll find associative arrays much easier to work with.
Unfortunately bash doesn't give you nested arrays, but you can do this:
declare -A MountVolumes=(
[b]='
[fs]=xfs
[mkfs_options]="foo bar baz"
[mnt_options]="foo bar baz"
[path]=/foo/bar/baz
'
[c]='
[fs]=xfs
[mkfs_options]=-"t really -foo /ugly/options"
[mnt_options]="-t really -foo /ugly/options"
[path]=/foo/bar/baz
'
)
for prefix in "${!MountVolumes[@]}"; do
declare -A "tmp=( ${MountVolumes[$prefix]} )"
echo "prefix=$prefix, mkfs_options=${tmp[mkfs_options]}"
done
outputs
prefix=c, mkfs_options=-t really -foo /ugly/options
prefix=b, mkfs_options=foo bar baz
I think this is a readable and maintainable way to go. Quoting may become more of an issue.
Since the variables are already in the environment, try this:
for prefix in {b..z}; do
if env | grep -q "^MountVolumes_${prefix}_"; then
declare -A tmp=()
for subvar in fs path mkfs_options mnt_options; do
var="MountVolumes_${prefix}_${subvar}"
tmp[$subvar]=${!var}
done
echo $prefix
declare -p tmp
fi
done
b
declare -A tmp=([path]="/foo/bar/baz" [fs]="xfs" [mnt_options]="foo bar baz" [mkfs_options]="foo bar baz" )
c
declare -A tmp=([path]="/foo/bar/baz" [fs]="xfs" [mnt_options]="" [mkfs_options]="-t really -foo /ugly/options" )
It would be helpful if the variables were spelled consistently though, not MountVolumes_c_mkfs_fs_options
On the other hand, if you only care about variable names:
prefix=c
tmp="MountVolumes_${prefix}_@"
eval varnames=( "\${!$tmp}" )
which of course is gross, but results in
$ declare -p varnames
declare -a varnames=([0]="MountVolumes_c_fs" [1]="MountVolumes_c_mkfs_fs_options" [2]="MountVolumes_c_mkfs_options" [3]="MountVolumes_c_path")
Upvotes: 1