ren*_*ack 0 linux bash shell variable-expansion single-quotes
我有一个特定于项目的命令,它以以下形式生成输出:
Parameter1='value1' Parameter2='Value2' ... #变量的单引号值。
但我想明确分配该值并需要打印必须显示相应值的参数。
这里xtc_cmd get是项目特定的 cmd
root@renway:~# FOO=`xtc_cmd get lan_ifname lan_ipaddr lan_netmask`
root@renway:~#
root@renway:~# echo $FOO
SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'
root@renway:~#
root@renway:~# echo $SYSCFG_lan_ifname
root@renway:~# echo $SYSCFG_lan_ipaddr
root@renway:~# echo $SYSCFG_lan_netmask
Run Code Online (Sandbox Code Playgroud)
但是,在变量打印它们的值之后,我尝试了“ eval $FOO ”。由于安全原因,我想跳过'eval'。
分享脚本执行的输出:
root@renway:~# /tmp/test.sh
++ xtc_cmd get lan_ifname lan_ipaddr lan_netmask
+ FOO='SYSCFG_lan_ifname='\''br1'\''
SYSCFG_lan_ipaddr='\''10.0.0.1'\''
SYSCFG_lan_netmask='\''255.255.255.0'\'''
+ echo 'SYSCFG_lan_ifname='\''br1'\''' 'SYSCFG_lan_ipaddr='\''10.0.0.1'\''' 'SYSCFG_lan_netmask='\''255.255.255.0'\'''
SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'
Run Code Online (Sandbox Code Playgroud)
如何实际分配值并打印这些变量。
输入感兴趣的字符串
FOO='SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\'' SYSCFG_lan_pd_interfaces='\''brlan0 brlan19 brlan20'\'''
Run Code Online (Sandbox Code Playgroud)
预期输出:
foo_SYSCFG_lan_ipaddr=10.0.0.1
foo_SYSCFG_sysdate='$(date>> /tmp/date.txt)0' #single quoted value
foo_SYSCFG_lan_pd_interfaces=brlan0 brlan19 brlan20 #whitespace separated string
Run Code Online (Sandbox Code Playgroud)
与其他参数相比,这里的挑战是SYSCFG_sysdate单独保存单引号值'$(date>> /tmp/date.txt)0'。
抱歉,我错过了最早强调或提及此参数。这是为了测试恶意命令注入攻击。所以这里的期望值是按原样存储但没有执行命令的值。使用 'eval' 内置命令,日期命令正在执行,这是意料之中的。
运行使用“set”内置的Zilog80 的 POSIX V1 脚本后,我得到了所需的输出。
但是 POSIX V2 脚本只有在没有SYSCFG_sysdate参数的情况下才能正常运行 。
特别感谢 @ Charles Duffy和 @ Zilog80对这个问题的大量宝贵意见和指导。
借用一个密切相关问题的答案(从字符串中正确读取引用/转义的参数):
#!/usr/bin/env bash
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'"
case $BASH_VERSION in
''|[1-3].*) echo "ERROR: Bash 4.0 required; this is ${BASH_VERSION:-not bash}" >&2; exit 1;;
esac
declare -A kwargs=( )
while IFS= read -r -d ''; do
[[ $REPLY = *=* ]] || {
printf 'ERROR: Item %q is not in assignment form\n' "$REPLY" >&2
continue
}
kwargs[${REPLY%%=*}]=${REPLY#*=}
done < <(xargs printf '%s\0' <<<"$FOO")
# show what we parsed for demonstration purposes
declare -p kwargs >&2
Run Code Online (Sandbox Code Playgroud)
您可以在https://ideone.com/KniaC4的在线沙箱中看到它的运行情况;它的输出是以下形式的关联数组:
declare -A kwargs=([SYSCFG_lan_ifname]="br1" [SYSCFG_lan_netmask]="255.255.255.0" [SYSCFG_lan_ipaddr]="10.0.0.1" )
Run Code Online (Sandbox Code Playgroud)
...因此您可以参考"${kwargs[SYSCFG_lan_ifname]}", 或"${kwargs[SYSCFG_lan_ipaddr]}"。
这比分配给常规 bash 变量更安全,因为它不会让攻击者修改 PATH、LD_PRELOAD 或其他修改 shell、链接器、加载器、标准 C 库等行为的环境变量(请注意,即使您不要明确地export由这段代码创建的赋值,赋值给一个已经导出的变量将自动导出新值;所以只适用于环境变量而不是常规 shell 变量的安全问题仍然可以在这里发挥作用)。
警告:xargs 解析字符串的方式与 POSIX sh 标准不太兼容——有关详细信息和其他选项,请参阅上面给出的链接(Python 有一个完全兼容的解析器 f/e,链接的答案描述了如何使用它来自 bash)。
当关联数组不可用时,可以为常规变量添加前缀:
#!/usr/bin/env bash
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'"
while IFS= read -r -d ''; do
[[ $REPLY = *=* ]] || {
printf 'ERROR: Item %q is not in assignment form\n' "$REPLY" >&2
continue
}
printf -v "foo_${REPLY%%=*}" '%s' "${REPLY#*=}"
done < <(xargs printf '%s\0' <<<"$FOO")
# show what we parsed for demonstration purposes
for var in ${!foo_*}; do
echo "$var has value: ${!var}"
done
Run Code Online (Sandbox Code Playgroud)
在https://ideone.com/7UZJkT 上看到这个运行,输出:
#!/usr/bin/env bash
FOO="SYSCFG_lan_ifname='br1' SYSCFG_lan_ipaddr='10.0.0.1' SYSCFG_lan_netmask='255.255.255.0'"
case $BASH_VERSION in
''|[1-3].*) echo "ERROR: Bash 4.0 required; this is ${BASH_VERSION:-not bash}" >&2; exit 1;;
esac
declare -A kwargs=( )
while IFS= read -r -d ''; do
[[ $REPLY = *=* ]] || {
printf 'ERROR: Item %q is not in assignment form\n' "$REPLY" >&2
continue
}
kwargs[${REPLY%%=*}]=${REPLY#*=}
done < <(xargs printf '%s\0' <<<"$FOO")
# show what we parsed for demonstration purposes
declare -p kwargs >&2
Run Code Online (Sandbox Code Playgroud)