I have some logic like:
if [[ -n "$SSH_CLIENT" ]]
then
sflag="-s $(echo "$SSH_CLIENT" | awk '{ print $1}')"
else
sflag=''
fi
iptables -A MY_RULE "$sflag" -p tcp -m tcp --dport 9999 -m conntrack -j ACCEPT
In other words, I want to mimic only passing the -s
flag to iptables
if SSH_CLIENT
is set. What actually happens is that the empty string is inadvertently passed.
I'm interested in whether it is possible, in the interest of not repeating two quite long iptables
calls, to expand the flag name and value. E.g. the command above should expand to
iptables -A MY_RULE -s 10.10.10.10 -p tcp -m tcp ...
, oriptables -A MY_RULE -p tcp -m tcp ...
The problem is that in the second case, the expansion actually becomes:
iptables -A MY_RULE '' -p tcp -m tcp
and there is an extra empty string that is treated as a positional argument. How can I achieve this correctly?
${var+ ...expansion...}
Using ${var+ ...words...}
lets you have an arbitrary number of words only if a variable is set:
iptables -A MY_RULE \
${SSH_CLIENT+ -s "${SSH_CLIENT%% *}"} \
-p tcp -m tcp --dport 9999 -m conntrack -j ACCEPT
Here, if-and-only-if SSH_CLIENT
is set, we add -s
followed by everything in SSH_CLIENT
up to the first space.
The more general approach is to use an array whenever you want to represent multiple strings as a single value:
ssh_client_args=( )
[[ $SSH_CLIENT ]] && ssh_client_args+=( -s "${SSH_CLIENT%% *}" )
iptables -A MY_RULE "${ssh_client_args[@]}" -p tcp -m tcp --dport 9999 -m conntrack -j ACCEPT
The syntax "${var%% *}
is a parameter expansion which expands to var
with the longest possible suffix starting with a space trimmed; thus, leaving the first word. This is much faster than running an external program like awk
. See also BashFAQ #100 describing general best practices for native-bash string manipulation.