我应该在我的 shell 脚本中避免使用 bash -c、sh -c 和其他 shell 的等效项吗?
should I avoid bash -c, sh -c, and other shells' equivalents in my shell scripts?
考虑以下代码:
#!/bin/bash -x
VAR='1 2 3'
bash -c "echo "$$VAR""
eval "echo "$$VAR""
bash -c "echo \"$$VAR\""
eval "echo \"$$VAR\""
输出:
+ VAR='1 2 3'
+ bash -c 'echo ' 2 3
3
+ eval 'echo ' 2 3
++ echo 2 3
2 3
+ bash -c 'echo " 2 3"'
2 3
+ eval 'echo " 2 3"'
++ echo ' 2 3'
2 3
似乎 eval
和 bash -c
都以相同的方式解释代码,即 "echo "$$VAR""
到 'echo ' 2 3
和 "echo \"$$VAR\""
到 'echo " 2 3"'
。
我似乎注意到的唯一区别是 bash -c
打开了一个子 shell,因此结果与 eval
不同。例如,在
bash -c 'echo ' 2 3
2和3是子shell的位置参数。另一方面,在
eval 'echo ' 2 3
它们只是 echo
的另一个论点。
所以我的问题是,-c
选项(bash -c
、sh -c
或其他 shell 的等效项)可以安全使用还是像 eval
?
据我所知,没有理由使用 bash shell 中的 bash -c
。使用它会启动一个新的进程,这是昂贵的。
您可以使用 eval,它不会启动新进程。如果你想要一个子shell(例如,为了保护环境)你可以使用括号。
通常,bash -c
(或其他带 -c 的 shells)用于从另一个非 shell 环境(或者可能是 [=解释的 DSL)执行命令=20=]) 其中需要对参数进行 shell 扩展。例如,在过去,您可能会在 C 程序中将它与 execvp
一起使用。如今,在大多数环境中通常有 运行 使用 shell 命令的方法。
是的,您应该避免在 shell 脚本中使用 sh -c
和等效项,就像您避免使用 eval
.
一样
eval "$foo"
...归根结底,这是一种安全风险,因为它将数据视为代码,从一开始就重新启动解析过程(因此,运行宁扩展,重定向等)。此外,由于在此过程中考虑了引用上下文,因此正在评估的数据中的内容能够转义引号、终止命令,并以其他方式试图逃避在安全方面所做的任何努力。
sh -c "$foo"
做同样的事情——运行宁扩展、重定向等——仅在全新的shell(不共享非导出变量或其他状态)中。
这两者都意味着 $(rm -rf /)
之类的内容很容易被扩展,除非非常小心,而不是确保——通常情况下——数据只会永远被视为数据,这是编写安全代码的基本要素。
现在,令人高兴的是,当您使用 bash
(或 zsh 或 ksh93)而不是 sh
时,您 可以 几乎在所有情况下都避免使用 eval
。
例如:
value=$(eval "echo $$varname")
...可以替换为:
value=${!varname}
...在 bash,
eval "$varname="'$value'
...可以替换为...
printf -v varname %s "$value"
...等等(ksh93 和 zsh 与后者有直接的等价物);涉及关联映射等的更高级公式最好通过新的 bash 4.3(和 ksh93)namevar 支持来解决。
值得注意的是,bash -c
不会 在上面的大多数示例中有效地替换 eval
,因为它不会 运行相同的上下文:当子进程退出时,对 shell 状态所做的更改将被丢弃;因此,bash -c
不仅不能买到安全性,而且 也不能 作为 eval
的替代品。
考虑以下代码:
#!/bin/bash -x
VAR='1 2 3'
bash -c "echo "$$VAR""
eval "echo "$$VAR""
bash -c "echo \"$$VAR\""
eval "echo \"$$VAR\""
输出:
+ VAR='1 2 3'
+ bash -c 'echo ' 2 3
3
+ eval 'echo ' 2 3
++ echo 2 3
2 3
+ bash -c 'echo " 2 3"'
2 3
+ eval 'echo " 2 3"'
++ echo ' 2 3'
2 3
似乎 eval
和 bash -c
都以相同的方式解释代码,即 "echo "$$VAR""
到 'echo ' 2 3
和 "echo \"$$VAR\""
到 'echo " 2 3"'
。
我似乎注意到的唯一区别是 bash -c
打开了一个子 shell,因此结果与 eval
不同。例如,在
bash -c 'echo ' 2 3
2和3是子shell的位置参数。另一方面,在
eval 'echo ' 2 3
它们只是 echo
的另一个论点。
所以我的问题是,-c
选项(bash -c
、sh -c
或其他 shell 的等效项)可以安全使用还是像 eval
?
据我所知,没有理由使用 bash shell 中的 bash -c
。使用它会启动一个新的进程,这是昂贵的。
您可以使用 eval,它不会启动新进程。如果你想要一个子shell(例如,为了保护环境)你可以使用括号。
通常,bash -c
(或其他带 -c 的 shells)用于从另一个非 shell 环境(或者可能是 [=解释的 DSL)执行命令=20=]) 其中需要对参数进行 shell 扩展。例如,在过去,您可能会在 C 程序中将它与 execvp
一起使用。如今,在大多数环境中通常有 运行 使用 shell 命令的方法。
是的,您应该避免在 shell 脚本中使用 sh -c
和等效项,就像您避免使用 eval
.
eval "$foo"
...归根结底,这是一种安全风险,因为它将数据视为代码,从一开始就重新启动解析过程(因此,运行宁扩展,重定向等)。此外,由于在此过程中考虑了引用上下文,因此正在评估的数据中的内容能够转义引号、终止命令,并以其他方式试图逃避在安全方面所做的任何努力。
sh -c "$foo"
做同样的事情——运行宁扩展、重定向等——仅在全新的shell(不共享非导出变量或其他状态)中。
这两者都意味着 $(rm -rf /)
之类的内容很容易被扩展,除非非常小心,而不是确保——通常情况下——数据只会永远被视为数据,这是编写安全代码的基本要素。
现在,令人高兴的是,当您使用 bash
(或 zsh 或 ksh93)而不是 sh
时,您 可以 几乎在所有情况下都避免使用 eval
。
例如:
value=$(eval "echo $$varname")
...可以替换为:
value=${!varname}
...在 bash,
eval "$varname="'$value'
...可以替换为...
printf -v varname %s "$value"
...等等(ksh93 和 zsh 与后者有直接的等价物);涉及关联映射等的更高级公式最好通过新的 bash 4.3(和 ksh93)namevar 支持来解决。
值得注意的是,bash -c
不会 在上面的大多数示例中有效地替换 eval
,因为它不会 运行相同的上下文:当子进程退出时,对 shell 状态所做的更改将被丢弃;因此,bash -c
不仅不能买到安全性,而且 也不能 作为 eval
的替代品。