为什么这有效:
[ -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
特殊参数):echo
、test
、&&
、reboot
,并且要运行的命令源自第一个 ( echo
) 以及作为参数传递给该命令的所有单词。&&
是一个论点echo
。只有未加引号的文字&&
才会被理解&&
为该语言的运算符sh
。就像在 C 中一样,并不意味着仅仅因为恰好包含C 语言语法中的运算符而var="foo, bar"; printf(var)
传递两个参数。printf
var
,
事实上,如果我们回顾历史,回到 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,如rc
、fish
或zsh
(类似于 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
函数用于代码,变量用于数据。