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

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

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

答案1

这既不奇怪,也不是一个错误bash(尽管它似乎确实是一个错误/usr/share/modules/init/bash)。您正在将不带引号的命令替换与eval.作为命令替换结果的字符串,由于未加引号,因此将进行分词和文件名扩展(通配符)。代码中的*)与 filename 匹配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 `('

shell中同样的事情yash

$ 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

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

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

只有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;' '};'

处理这个问题的正确方法是来源剧本foo.sh

. ./foo.sh

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
}

轻松破坏此命令的另一种方法没有创建一个特殊命名的文件,是将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'

这会破坏它,因为在评估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 `)'

有关的:

相关内容