在不使用eval的情况下,将Bash变量插值到heredoc /多行变量中的安全方法是什么?

时间:2019-10-10 20:36:17

标签: bash sed eval string-interpolation

所以我有一个疯狂的想法,就是将文档嵌入脚本中(这对我来说不是什么新鲜事物,只是新内容),然后使用sed解析它们。我可以通过用行定界符注释掉一段文本,然后在将字符串打印到stdout时弹出注释哈希(在每一行上)来实现此目的。

以一个示例为例,想象这样一个shell脚本myscript.sh

   #!/usr/bin/env bash
   #---- doc:start ----#
   # Hello $User\n     
   # This is myscript.sh
   # ${red}This will be red!${eol}
   #---- doc:end   ----#
   printf "Hello, World!"

我有一个sed脚本,该脚本可以捕获线里程表之间的所有内容,并将其放入多行变量$doc

使用这个变量,我想对任何局部变量(不一定是导出的变量),转义码,术语颜色等进行插值,并且在查看如何执行此操作时,我唯一能找到的答案就是使用eval;这是一个工作示例(在这里使用字符串):

  while IFS= read -r line; do
    line=$(eval "echo -e \"$line\""); 
    echo "$line"
  done  <<< "$doc"; # doc is a multiline variable

但是,如果有人在此文本块中添加$(rm -rf ..),则会执行该文本;这种模式似乎允许其他不安全方案。

我研究的另一种方法是使用printf -v line ...动态分配变量,但是无法成功进行变量替换。

我想在Bash 3.2上下文中实现此目标,而不必安装任何其他工具或实用程序;尽可能使用POSIX,但是Bashism和通用实用程序也可以。 想要在Mac和Ubuntu上都可以使用

也许正则表达式可以排除任何看起来像可执行命令(或超级转义可疑字符串)的东西。

更新Link to "working" code

2 个答案:

答案 0 :(得分:3)

使用GNU gettext包中的envsubst

这仅要求您导出应该被替换的变量。以下用法演示了在red调用期间将常规shell变量eolenvsubst临时提升为环境变量:

red="$red" eol="$eol" envsubst <<<'EOF'
...${red}...${eol}...
EOF

答案 1 :(得分:0)

听起来像您真正需要的是模板,而不是变量插入。也就是说,您可以明确地逐个变量地执行此操作,这可能会提供额外的安全性。

#!/usr/bin/env bash
#---- doc:start ----#
# Hello $User\n
# This is myscript.sh
# ${red}This will be red!${eol}
#---- doc:end   ----#

# template replacemenmts
red=$'\e[31m'
norm=$'\e[m'
eol=$'\n'

# fetch and trim...
s=$(< $0)
s=${s%%#---- doc:end   ----#*}
s=${s##*#---- doc:start ----#}

# do substitutions
s=${s//\$User/$USER}
s=${s//\${red\}/$red}
s=${s//\${eol\}/$eol}
s=${s//\\n/$eol}

printf '%s\n%s' "$s" "$norm"

这避免了使用sed或其他可能因平台而异的其他东西。 (例如,sed在GNU,FreeBSD / MacOS和Solaris之间是不同的。)

只需考虑一下here's an example,我就倾向于将文档嵌入bash脚本中。 (是的,我使用sed。:-))