仅在设置值时扩展为标志

Expanding to a flag only if value is set

我有这样的逻辑:

if [[ -n "$SSH_CLIENT" ]]
then
    sflag="-s $(echo "$SSH_CLIENT" | awk '{ print }')"
else
    sflag=''
fi

iptables -A MY_RULE "$sflag" -p tcp -m tcp --dport 9999 -m conntrack -j ACCEPT

换句话说,如果设置了 SSH_CLIENT,我想模拟仅将 -s 标志传递给 iptables。实际发生的是无意中传递了空字符串。

我感兴趣的是,为了不重复两个相当长的 iptables 调用,是否有可能扩展标志 name 和 value。例如。上面的命令应该扩展为

问题是在第二种情况下,展开实际上变成了:

iptables -A MY_RULE '' -p tcp -m tcp

并且有一个额外的空字符串被视为位置参数。我怎样才能正确地做到这一点?

这是您可能不想引用变量扩展的情况之一:

iptables -A MY_RULE $sflag -p tcp -m tcp ...

或者,我们可以使用 ${var+...} 扩展:

 iptables -A MY_RULE ${SSH_CLIENT:+-s "${SSH_CLIENT%% *}"} -p tcp -m tcp ...

读作"if $SSH_CLIENT is set (and not null) then expand and substitute -s "${SSH_CLIENT%% *}", else nothing"。

请注意,我们不引用 $.+ 扩展,但我们会在需要的地方引用其中的各个参数(显然 -s 不需要引号,尽管您可以随意如果需要,请添加它们)。

我删除了外部 awk 命令,因为 $.%% 扩展是截断字符串的更简单、更有效的方法。

所有 POSIX 外壳:使用 ${var+ ...expansion...}

使用 ${var+ ...words...} 仅当设置了变量时,您才可以拥有任意数量的单词:

iptables -A MY_RULE \
  ${SSH_CLIENT+ -s "${SSH_CLIENT%% *}"} \
  -p tcp -m tcp --dport 9999 -m conntrack -j ACCEPT

这里,设置了if-and-only-if SSH_CLIENT,我们添加-s,然后添加SSH_CLIENT中的所有内容,直到第一个 space.


Bash(和其他扩展 shell):使用数组

更通用的方法是在您希望将多个字符串表示为单个值时使用数组:

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

语法 "${var%% *}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 描述原生 bash 字符串操作的一般最佳实践。