是否允许 shell 优化无用的终止命令?

Iwi*_*ist 27 shell standard posix command

如果 shell 被要求执行一个已知会终止的可能无用(或部分无用)的命令,例如cat hugeregularfile.txt > /dev/null,它是否可以跳过该命令的执行(或执行更便宜的等价物,例如, touch -a hugeregularfile.txt)?

更一般地说,shell 是否类似于 C 编译器,因为它可以对源代码执行任何转换,只要外部可观察的行为就像抽象机器评估它一样?

编辑

Nota Bene:我最初提出的问题有一个标题,询问是否允许shell进行这些优化,而不是是否应该甚至是否存在可以执行这些优化的实现。我对理论比实践更感兴趣,尽管两者都是受欢迎的。

sca*_*cai 26

不,那是个坏主意。

cat hugeregularfile.txt > /dev/null并且touch -a hugeregularfile.txt不一样。cat将读取整个文件,即使您将输出重定向到/dev/null. 阅读整个文件可能正是您想要的。例如为了缓存它,以便以后的读取速度会明显加快。外壳无法知道您的意图。

同样,C 编译器永远不会优化读取文件,即使您不查看所读取的内容。

  • @cHao 甚至 `true` 和 `false` 都设置了 `$?`。 (7认同)
  • @IwillnotexistIdonotexist 不,外壳无法看到即将发生的一切。它不知道`cat`。事实上,`cat` 可以做任何事情,从格式化硬盘驱动器到下载互联网。 (5认同)
  • “Unix 的设计目的不是阻止用户做愚蠢的事情,因为这也会阻止他们做聪明的事情。” — 道格·格温 (5认同)
  • 正如@scai 上面指出的那样,可执行文件不像语言关键字:`cat` 和 `/dev/null` 具有典型的含义,但它们并不能_保证_以这种方式行事。为了在保证不改变预期行为的同时执行优化,只能允许优化涉及在 shell 本身内实现的构造,而不是在执行环境中找到的东西......无论它们的名字看起来多么直观。 (3认同)
  • @Iwillnotexist:每个有用的命令(除了可以说是“true”和“false”)都有潜在的副作用,而且副作用几乎总是调用命令的重点。如果不解决停机问题,shell 就无法提前知道这些副作用(对于像 `cat` 这样的外部程序)。所以它正确地没有尝试,并假设你的意思是你所说的。 (2认同)

Ant*_*hon 20

不,因为/dev/null它只是一个名称,可以用于任何其他设备或文件,而不是“通常”是数据接收器。

因此,shell(或任何其他程序)根据名称不知道它正在写入的文件是否正在对数据进行“真实”操作。AFAIK也没有shell程序可以进行的系统调用,以确定例如文件描述符实际上没有做任何事情。

您与优化 C 程序中的代码的比较不起作用,因为 shell 没有 C 编译器对一段源代码的总体概述。shell/dev/null对优化您的示例的了解不够,更像是 C 编译器对它动态链接到的函数调用中的代码不够了解,因此无法进行调用。

  • 事实证明,ksh93 有时会特别对待`/dev/null`。将其标准输出定向到 `/dev/null` 的内置函数,例如 `echo foo >/dev/null`,不会导致对 `/dev/null` 进行任何写入。如果它调用非内置命令(例如`cat file >/dev/null`),它不会做任何特殊的事情。 (4认同)
  • 实际上`/dev/null`是极少数的[标准化路径](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap10.html)之一,还有`/dev/tty`、`/ dev/console`、`/tmp`、`/dev/` 和 `/`。 (3认同)
  • @MarkPlotnick 实际上 `cat` _is_ 是一个 ksh93 内置函数(除非你将 `/opt/ast/bin` 放在 `/bin` 之前(或在 `$PATH` 中任何可用的 `cat`)。是的,尽管带有该内置函数的 `cat file > /dev/null` 确实会`读取``file` 的内容,但它确实_不_将其写入 /dev/null(尽管它会打开并对其进行 fstats)。 (2认同)

Sté*_*las 15

它不会优化正在运行的命令(并且您已经收到了许多很好的答案,告诉您为什么不应该这样做),但在某些情况下它可能会优化分叉、管道/套接字对、读取。它可能进行的优化类型:

  • 对于一些现代 shell,除非trap设置了某些s,否则脚本中的最后一个命令可以在 shell 的进程中执行。例如在 中sh -c ls,大多数 sh实现(bashmkshkshzshyash、 的某些版本ash)不会派生一个进程来运行ls
  • 在 中ksh93,在调用外部命令之前,命令替换不会创建管道或派生进程($(echo foo)例如将扩展为foo没有管道/套接字对或派生)。
  • 如果read某些 shell ( bash, AT&T ksh)的内置函数检测到 stdin 是可查找的,它们将不会进行单字节读取(在这种情况下,它们将进行大量读取并返回到它们要读取的内容的末尾)。

  • @yoniYalovitsky,这是_原始原因_。`ksh93` 是在 _optimisation_ 方面处于领先地位的 shell,因为它的目标是/曾经被视为与诸如 `perl` 之类的编程语言相提并论。所以你可以查看 `ksh` 文档、代码(祝你好运)和邮件列表以获取更多信息。 (3认同)

Pet*_*ris 7

当看到 时cat hugeregularfile.txt > /dev/null,不允许 shell 相信这个动作是无用的——cat它不是 shell 的一部分,在理论上和实践中都可以做任何事情。

例如,用户可能已将可执行文件重命名rmcat,并且该行突然执行外部可观察行为,即删除文件。

用户可能已经编译了一个cat进入无限循环的版本,因此 shell 不能假设它如您所建议的那样“已知终止”。

有人可能已经安装了一个cat按预期工作的版本,但是如果它以足够的权限运行,安装 rootkit 会带来额外的副作用——同样,shell 应该适当地执行它。

  • 实际上,`mksh` 实际上确实优化了 `V=$(cat file)`,使其成为内置函数。所以外壳可能会优化它,但不会*转换*它只是一个`touch -a`。 (2认同)