Update: I forgot to quote the embedded $2
in the example. It should now work as requested by the OP. Also, the point behind this answer and the use of eval
is to provide a portable example using standard Bourne shell syntax across multiple different implementations. I have tested all the following code across bash, dash, and zsh. Dash is a better test of portability as it uses a much more limited and more true to Bourne shell syntax. It is also the default shell on Debian/Ubuntu for /bin/sh
. The typeset
and printf -v
do not exist in Dash. I do think eval
is ugly, but it was the first mechanism in Bourne shell and is the best method for true portability.
While I think that associative arrays are the way to go, I want to provide an exact answer to his original question and one that is portable to other Bourne shells besides Bash. This here would be how dynamic variable names would be done traditionally:
for chain in http https ssh; do
eval $chain="\"\$(iptables -nvxL $chain | tail -1 | awk '{print \$2}')\""
done
The eval
operator in Bourne shell is used to substitute variables into a command and then re-run (re-evaluate) it as a command after that is complete. After the first substitution of that command, the $chain variable and first set of quoting is completed leaving it to look like this:
http="$(iptables -nvxL http | tail -1 | awk '{print $2}')"
Which then evaluates as a variable assignment to a command expansion of iptables
. Since the inner command used single-quotes, I couldn't use them to quote the eval
statement, hence the use of "\"
and \""
to quote it for eval and to pass the double and single quotes through as needed in the resulting command. The double-quotes around the command expansion might not be needed, but I have run into issues with some shell if the command expands into multiple words. To dump out the contents of these, do something similar:
for chain in http https ssh; do
eval echo \$$chain
done
The first dollar-sign is quoted and passed though. This results in the following three commands being executed after the loop is un-rolled:
echo $http
echo $https
echo $ssh
awk 'END {print $2}'
in place oftail -1 | awk '{print $2}'
. – Dennis Williamson Nov 22 '22 at 18:44