我今天遇到一个奇怪的错误,在一个目录中运行脚本时,该目录中包含一个带括号的目录,例如a()
.
最小的工作示例
我设法将错误减少到以下最小的工作示例:
/tmp
在其中创建一个空目录cd
:
mkdir /tmp/foo
cd /tmp/foo
在其中创建一个名为foo.sh的脚本,其中包含:
foo() {
somevar=1;
case somevar in
aha) echo "something" ;;
*) echo "other" ;;
esac;
};
运行以下命令:
eval $(/bin/cat foo.sh)
不应该有任何错误。
创建一个带括号的文件:
touch "a()"
再次运行命令:
eval $(/bin/cat foo.sh)
我现在得到错误:
bash: syntax error near unexpected token `('
为什么 bash 甚至关心目录中的文件?为什么括号会导致错误?
系统信息:
$ bash --version
GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)
Copyright © 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.1 LTS
Release: 18.04
Codename: bionic
更详细的背景和原始错误:
我的问题来自使用/usr/share/modules/init/bash
从environment-modules
包中获取的脚本,总结如下:
$ dpkg -l environment-modules
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-=============================================================-===================================-===================================-================================================================================================================================
ii environment-modules 4.1.1-1 amd64 Modular system for handling environment variables
$ source /usr/share/modules/init/bash
$ touch "a()"
$ source /usr/share/modules/init/bash
bash: eval: line 43: syntax error near unexpected token `('
bash: eval: line 43: ` a() _mlshdbg='' ;;'
这既不奇怪也不是一个错误
bash
(虽然它似乎确实是一个错误/usr/share/modules/init/bash
)。您正在使用不带引号的命令替换和eval
. 作为命令替换结果的字符串,因为它没有被引用,所以会进行分词和文件名扩展(globbing)。*)
代码中的 与 filename 匹配,a()
因此在文件名扩展阶段用此文件名替换。在下面运行您的示例
set -x
突出显示了这一点:yash
外壳中的相同内容:并与
ksh93
:并且
dash
:只有
zsh
会处理这个,因为它不执行 globbing:处理此问题的正确方法是获取脚本
foo.sh
:据我所知,真的没有理由使用
eval "$(cat foo.sh)"
。这也是一个代码注入漏洞:
在不创建特殊命名文件的情况下轻松破坏此命令的另一种方法是将
IFS
变量设置为默认值以外的一组字符:这打破了它,因为在评估
eval
. 使用IFS=';{} '
,这些字符中的每一个都将用于将文本拆分foo.sh
为单词(然后这些字符将从字符串中删除)。甚至
zsh
不会对此免疫:有关的: