工作目录中带有括号/方括号的文件会导致 eval 错误

KIA*_*aze 5 bash eval module

我今天遇到了一个奇怪的错误,在包含带有括号的目录的目录中运行脚本时,例如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/bashenvironment-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)

yashshell中同样的事情:

$ 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)

有关的: