linux - 在没有 eval 的情况下从 bash 中的“key1='val1' key2='val2'”字符串中解析变量
问题描述
我有一个特定于项目的命令,它产生以下形式的输出:
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
但是,在变量打印它们的值之后,我尝试了“ eval $FOO ”。由于安全原因,我想跳过“评估”。
分享脚本执行的输出:
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'
如何实际分配值并打印这些变量。
输入感兴趣的字符串
FOO='SYSCFG_lan_ipaddr='\''10.0.0.1'\'' SYSCFG_sysdate='\'''\''$(date>> /tmp/date.txt)0'\'''\'' SYSCFG_lan_pd_interfaces='\''brlan0 brlan19 brlan20'\'''
预期输出:
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
这里的挑战是SYSCFG_sysdate与其他参数相比,仅包含单引号值'$(date>> /tmp/date.txt)0' 。
抱歉,我错过了最早强调或提及这个参数。这是为了测试恶意命令注入攻击。所以这里的期望是要按原样存储但没有命令执行的值。使用 'eval' 内置,日期命令正在执行,这是不期望的。
我在运行 Zilog80 的 POSIX V1 脚本后得到了所需的输出,该脚本使用了内置的“set”。
但是 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
您可以在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" )
...所以你可以参考"${kwargs[SYSCFG_lan_ifname]}"
, 或"${kwargs[SYSCFG_lan_ipaddr]}"
。
这比分配给常规 bash 变量更安全,因为它不允许攻击者修改 PATH、LD_PRELOAD 或其他修改 shell、链接器、加载器、标准 C 库等行为的环境变量。(请注意,即使您不要显式地export
指定此代码创建的分配,分配给已导出的变量将自动导出新值;因此,仅适用于环境变量而不适用于常规 shell 变量的安全问题仍然可以在这里发挥作用)。
警告:xargs 解析字符串的方式与 POSIX sh 标准不太兼容——有关详细信息和其他选项,请参阅上面给出的链接(Python 有一个完全兼容的解析器 f/e,链接的答案描述了如何使用它来自 bash)。
或者,使用较旧的 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
在https://ideone.com/7UZJkT看到这个运行,输出:
foo_SYSCFG_lan_ifname has value: br1
foo_SYSCFG_lan_ipaddr has value: 10.0.0.1
foo_SYSCFG_lan_netmask has value: 255.255.255.0
推荐阅读
- python - Python Kivy 不会使用 SDL2,坚持使用 pygame
- vue.js - 在 Vuetify 滑块中将文本与不同方向对齐
- python - TypeError:“NoneType”对象不可迭代
- java - Kotlin方式接口中的Java变量
- haskell - 有没有办法告诉堆栈我想默认使用较旧的解析器?
- perl - 保留图像中的纵横比::Magick 的缩略图功能
- ruby-on-rails - Webpacker 编译失败 - 错误:[Errno 21] 是目录:'bin'
- swift - SwiftUI TextField 键盘闪烁并中断其他语言的输入
- reactjs - 错误:仅在构建电子应用程序时发生写入 EPIPE
- node.js - 如何使用 Cors 或任何其他节点模块限制对我的 graphql 端点的访问