7

在 sh 中:

~$ `echo ls`
bin/  Desktop/

但在鱼中:

fish: Illegal command name “(echo ls)”
~% (echo ls)

(请注意,错误消息出现在命令行上方。)

~% echo (echo ls)
ls
~% eval (echo ls)
bin/  Desktop/

fish: Illegal command name “(echo ls)”
exec (echo ls)
     ^
~% exec (echo ls)

似乎命令替换只能作为命令的参数,而不是命令本身?为什么?

嗯,帮助文档确实说

如果参数包含一组括号,则括号内的文本将被解释为命令列表。

但是,为什么?

4

2 回答 2

7

更新

这个答案是十年前的 2010 年写的。最新版本的鱼​​(我在 3.1.2 上测试过)更新并且set cmd ls; $cmd现在有效。

如何

这是因为命令替换属于参数扩展并且不允许作为命令。

一个类似的例子:

在 sh 中:

tmpls=ls
$tmpls

但在鱼中:

% set cmd ls; $cmd
fish: Variables may not be used as commands.
...

为什么

简而言之,它有利于可验证性

这篇文章解释了细节:

由于允许在常规 shell 中使用变量作为命令,因此不可能可靠地检查脚本的语法。例如,这段 bash/zsh 代码可能合法也可能不合法,这取决于你的运气。你觉得幸运吗?

    if true; then if [ $RANDOM -lt 1024 ]; then END=fi; else END=true; fi; $END

当用户按下回车键时,bash 和 zsh 都尝试确定当前缓冲区中的命令是否完成,但由于此类问题,它们有时会失败。更糟糕的是,这段完全合法的代码被 bash 拒绝了:

  FI=fi; foo() { if true; then true; $FI; }

Fish 避免了这种问题,因为变量不允许作为命令。可以使用 eval 命令或使用函数以更简洁的方式将变量作为命令执行的任何操作。

出于同样的原因,命令替换不允许作为命令。

(注意:引用的例子是不公平的,因为 'if' 和 'fi' 不是简单的命令,而是保留字。见下面的评论。)

于 2010-07-19T15:58:54.907 回答
0

它与扩展的顺序有关。

来自:help expand-command-substitution_fish

组合多个参数展开时,展开按以下顺序执行:

 * Command substitutions
 * Variable expansions
 * Bracket expansion
 * Pid expansion
 * Wildcard expansion

扩展是从右到左执行的,嵌套括号扩展是从内向外执行的。

来自man bash

扩展的顺序是:大括号扩展、波浪号扩展、参数、变量和算术扩展以及命令替换(以从左到右的方式完成)、分词和路径名扩展。

于 2010-07-19T14:02:24.870 回答