bash: [: 当测试存储在变量中时缺少 `]'

bash: [: 当测试存储在变量中时缺少 `]'

为什么这有效:

[ -r /tmp ] && echo "tt" >/tmp/taa
cat taa
tt

但以下内容没有,如何解决这个问题,保留变量以实现可重用?

COMD='[ -r /tmp ] && echo "tt"'
$COMD >/tmp/taa
bash: [: missing `]

谢谢

编辑:eval确实有效。但是,我不明白为什么,并且在问题,我的被标记为重复,也没有任何解释。此外,如果&&删除以 开头的部分,它就可以工作,甚至实际上返回正确的结果,而无需eval

COMD='[ -r /tmp ]'
$COMD
echo $?
0

COMD='[ -r /tmpdddd ]'
$COMD
echo $?
1

答案1

你不会想到:

var='echo test && reboot'
echo $var

要输出test然后重新启动,可以吗?

相反,它输出echo test && reboot

这对于

var='echo test && reboot'
$var

现在,令人困惑的是,在大多数类似 Bourne 的 shell 中,它不会因echo test && reboot未找到该命令的错误而失败。相反,除非您修改了$IFS,否则它会输出test && reboot

这是因为,在这些 shell 中,将变量不加引号会应用某种 split+glob 运算符。

因为$var未加引号,所以它被分成单词(根据$IFS特殊参数):echotest&&reboot,并且要运行的命令源自第一个 ( echo) 以及作为参数传递给该命令的所有单词。&&是一个论点echo。只有未加引号的文字&&才会被理解&&为该语言的运算符sh。就像在 C 中一样,并不意味着仅仅因为恰好包含C 语言语法中的运算符而var="foo, bar"; printf(var)传递两个参数。printfvar,

事实上,如果我们回顾历史,回到 70 年代初的 Unix 的第一个版本,它们的sh(现在称为汤普森壳)会跑到reboot那里。嗯,sh当时不支持变量,但它确实有位置参数。

这里在 PDP11 模拟器上使用 Unix V1:

$ cat myscript
echo $1
$ sh myscript 'echo test; reboot'
echo test
No command
$ ls -l /bin/sh
total   12
 88 lxrwr-  1 sys    5312 Jan  1 00:00:00 sh

reboot(值得庆幸的是,当时没有命令,因此出现了No command错误)。

以今天的标准来看,这听起来可能很疯狂,但请记住,Unix V1 运行在具有几百千字节内存和兆字节存储空间的机器上,因此当时的事情必须保持简单。看看它如何sh明显小于/bin/true现代系统上的(不执行任何操作的命令)(我的系统上为 27KB(不包括动态链接器或它链接到的动态库),而该 sh 为 5KB)。

70 年代末的 Unix V7 附带了 Bourne shell,sh当时还是新的。更先进,但仍然没有完全破坏与其前身的兼容性。对于 BSD 上几乎同时出现的 csh 来说也是如此。这可能是 Bourne shell 的尴尬行为的主要原因:参数扩展仍然经历某些形式的扩展,例如拆分为单词和通配符(但不像早期 Unix shell 那样完全重新解释 sh 语言)这太疯狂了),为了向后兼容 Thomson shell。

一些 shell,如rcfishzsh(类似于 Bourne 的 shell)最终修复了这个问题,但许多 shellbash都保持了与 Bourne shell 的向后兼容性。

现在,如果您想将字符串解释为 shell 代码,则该eval命令的用途如下:

eval "$var"

(注意引号,以防止在类似 Bourne 的 shell 中出现 split+globbing)。

但是,如果您想将代码存储在变量中,那么像其他语言一样使用函数会更有意义:

f() { echo test && reboot; }

答案2

因为&&变量扩展后运算符不被识别。引号也不是:如果有效,它将打印"tt"。 (尝试:cmd='echo "tt"'; $cmd

让它成为一个函数:

checktmp() {
    [ -r /tmp ] && echo "tt"
}
checktmp

函数用于代码,变量用于数据。

相关内容