仅在设置值时扩展为标志
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 -s 10.10.10.10 -p tcp -m tcp ...
,或
iptables -A MY_RULE -p tcp -m tcp ...
问题是在第二种情况下,展开实际上变成了:
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 字符串操作的一般最佳实践。
我有这样的逻辑:
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 -s 10.10.10.10 -p tcp -m tcp ...
,或iptables -A MY_RULE -p tcp -m tcp ...
问题是在第二种情况下,展开实际上变成了:
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 字符串操作的一般最佳实践。