我今天遇到了一个奇怪的错误,在包含带有括号的目录的目录中运行脚本时,例如a()
.
最小工作示例
我设法将错误减少到以下最小工作示例:
在创建一个空目录/tmp
,并cd
把它:
mkdir /tmp/foo
cd /tmp/foo
Run Code Online (Sandbox Code Playgroud)
在其中创建一个名为foo.sh的脚本,其中包含:
foo() {
somevar=1;
case somevar in
aha) echo "something" ;;
*) echo "other" ;;
esac;
};
Run Code Online (Sandbox Code Playgroud)
运行以下命令:
eval $(/bin/cat foo.sh)
Run Code Online (Sandbox Code Playgroud)
不应该有任何错误。
创建一个带括号的文件:
touch "a()"
Run Code Online (Sandbox Code Playgroud)
再次运行命令:
eval $(/bin/cat foo.sh)
Run Code Online (Sandbox Code Playgroud)
我现在得到错误:
bash: syntax error near unexpected token `('
Run Code Online (Sandbox Code Playgroud)
为什么 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
Run Code Online (Sandbox Code Playgroud)
更详细的背景和原错误:
我的问题来自使用/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='' ;;'
Run Code Online (Sandbox Code Playgroud)
Kus*_*nda 13
这既不奇怪也不是错误bash
(尽管它似乎确实是错误/usr/share/modules/init/bash
)。您正在使用不带引号的命令替换与eval
. 作为命令替换结果的字符串,因为它没有被引用,将经历分词和文件名扩展(通配)。所述*)
代码中的文件名相匹配a()
,所以它是由该文件名中的文件名扩展阶段替换。
在下面运行您的示例set -x
强调了这一点:
$ eval $(cat foo.sh)
++ cat foo.sh
+ eval 'foo()' '{' 'somevar=1;' case somevar in 'aha)' echo '"something"' ';;' 'a()' echo '"other"' ';;' 'esac;' '};'
bash: syntax error near unexpected token `('
Run Code Online (Sandbox Code Playgroud)
在yash
shell中同样的事情:
$ eval $(cat foo.sh)
+ cat foo.sh
+ eval 'foo()' '{' 'somevar=1;' case somevar in 'aha)' echo '"something"' ';;' 'a()' echo '"other"' ';;' 'esac;' '};'
eval:1: syntax error: `)' is missing
eval:1: syntax error: `esac' is missing
eval:1: syntax error: `}' is missing
Run Code Online (Sandbox Code Playgroud)
并与ksh93
:
$ eval $(cat foo.sh)
+ cat foo.sh
+ eval 'foo()' '{' somevar='1;' case somevar in 'aha)' echo '"something"' ';;' 'a()' echo '"other"' ';;' 'esac;' '};'
ksh93: eval: syntax error: `(' unexpected
Run Code Online (Sandbox Code Playgroud)
并且dash
:
$ eval $(cat foo.sh)
+ cat foo.sh
+ eval foo() { somevar=1; case somevar in aha) echo "something" ;; a() echo "other" ;; esac; };
dash: 1: eval: Syntax error: "(" unexpected (expecting ")")
Run Code Online (Sandbox Code Playgroud)
只有zsh
会处理这个,因为它不执行通配符:
$ eval $(cat foo.sh)
+zsh:2> cat foo.sh
+zsh:2> eval 'foo()' '{' 'somevar=1;' case somevar in 'aha)' echo '"something"' ';;' '*)' echo '"other"' ';;' 'esac;' '};'
Run Code Online (Sandbox Code Playgroud)
以正确的操作方法是将源的foo.sh
脚本:
. ./foo.sh
Run Code Online (Sandbox Code Playgroud)
eval "$(cat foo.sh)"
据我所知,真的没有理由使用。
这也是一个代码注入漏洞:
$ touch '*) echo "hello" ;; *)'
$ eval $(cat foo.sh)
$ declare -f foo
foo ()
{
somevar=1;
case somevar in
aha)
echo "something"
;;
*)
echo "hello"
;;
*)
echo "other"
;;
esac
}
Run Code Online (Sandbox Code Playgroud)
无需创建特殊命名文件即可轻松破坏此命令的另一种方法是将IFS
变量设置为默认字符以外的一组字符:
$ IFS=';{} '
+ IFS=';{} '
$ eval $(cat foo.sh)
++ cat foo.sh
+ eval 'foo()' '
' somevar=1 '
' case somevar 'in
' 'aha)' echo '"something"' '' '
' '*)' echo '"other"' '' '
' esac '
' ''
bash: syntax error near unexpected token `somevar=1'
Run Code Online (Sandbox Code Playgroud)
这打破了它,因为在对eval
. 使用IFS=';{} '
,这些字符中的每一个都将用于将文本拆分foo.sh
为单词(然后这些字符将从字符串中删除)。
甚至zsh
不会对此免疫:
$ IFS=';{} '
+zsh:2> IFS=';{} '
$ eval $(cat foo.sh)
+zsh:3> cat foo.sh
+zsh:3> eval 'foo()' $'\n' 'somevar=1' $'\n' case somevar $'in\n' 'aha)' echo '"something"' '' $'\n' '*)' echo '"other"' '' $'\n' esac $'\n' '' ''
zsh: parse error near `)'
Run Code Online (Sandbox Code Playgroud)
有关的: