14

Perl 手册描述了一个可以在 csh、sh 或 Perl 中的任何一个下工作的完全不正确的结构,例如

eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"$@"}'
    & eval 'exec /usr/bin/perl -wS $0 $argv:q'
    if $running_under_some_shell;

确实很狡猾...有人可以详细解释一下这是如何工作的吗?

4

2 回答 2

27

这个想法是,如果这三行代码在标准 Bourne shell (sh)、C shell (csh) 或 Perl 中进行评估,它们会做 3 种不同的事情。仅在不支持#!在脚本开头使用一行指定解释器名称的系统上才需要此 hack。如果您以这 3 行开头的 Perl 脚本作为 shell 脚本执行,则 shell 将启动 Perl 解释器,将脚本的文件名和命令行参数传递给它。

在 Perl 中,三行构成一个语句,以 , 结尾,;形式为

eval '...' && eval '...' & eval '...' if $running_under_some_shell;

由于脚本刚刚启动,$running_under_some_shellis undef,这是错误的,并且永远不会执行评估。这是一个无操作。

狡猾的部分是$?0在 sh 和 csh 中的解析方式不同。在 sh 中,这意味着$?(最后一个命令的退出状态)后跟 0。由于没有上一个命令,$?将为 0,因此$?0计算结果为00。在 csh 中,$?0是一个特殊变量,如果当前输入文件名已知,则为 1,否则为 0。由于 shell 正在从脚本中读取这些行,$?0因此将为 1。

因此,在 sh 中eval '(exit $?0)'表示eval '(exit 00)',在 csh 中表示eval '(exit 1)'。括号表示应该在子shell 中评估退出命令。

sh 和 csh 都理解&&为“执行上一个命令,然后仅当上一个命令退出 0 时才执行以下命令”。所以只有 sh 会执行eval 'exec perl -wS $0 ${1+"$@"}'。csh 将进入下一行。

csh 将忽略行首的“&”。(我不确定这对 csh 到底意味着什么。它的目的是从 Perl 的角度将它变成一个单一的表达式。)然后 csh 继续进行评估eval 'exec /usr/bin/perl -wS $0 $argv:q'

这两个命令行非常相似。 exec perl表示通过启动perl. 与(启用警告)和(在 中查找指定脚本)的-wS含义相同。 是脚本的文件名。最后两者都生成当前命令行参数的副本(分别在 sh 和 csh 中)。-w-S$PATH$0${1+"$@"}$argv:q

它使用${1+"$@"}而不是更常见"$@"的方法来解决一些古老版本的 Bourne shell 中的错误。他们的意思是一样的。您可以阅读Bennett Todd 的解释中的详细信息(复制到 gbacon 的答案中)。

于 2010-02-22T06:54:57.790 回答
10

来自 Tom Christiansen 的收藏,远超你想知道的一切……</a>:

为什么我们使用eval 'exec perl $0 -S ${1+"$@"}'

新闻组:comp.lang.tcl,comp.unix.shell
发件人:bet@ritz.mordor.com (Bennett Todd)
主题:回复:"$@"vs ${1+"$@"}
Followup-To:comp.unix.shell
日期:1995 年 9 月 26 日星期二 14:35 :45 GMT
消息 ID:<DFIOJL.934@ritz.mordor.com>

(这不是一个真正的 TCL 问题;这是一个 Bourne Shell 问题;所以我已经交叉发布并设置了跟进,到 comp.unix.shell)。

曾几何时(或故事如此),某处有一个 Bourne Shell,它为插入整个命令行提供了两种选择。最简单的是$*,它只是在所有 args 中插入,丢失了任何保护内部空格的引用。它还提供"$@", 以保护空白。现在 icko 位是如何"$@"实现的。在这个早期的 shell 中,两个字符的序列$@将插入为

$1" "$2" "$3" "$4" ... $n

因此,当您添加周围的引号时,它会引用整个 schmeer。可爱,可爱,太可爱了....现在考虑一下正确的用法

"$@"

如果没有 args将扩展为:

""

那是空字符串——长度为零的单个参数。这与根本没有 args 不同。因此,有人想出了另一个 Bourne Shell 功能的巧妙应用,即条件插值。成语

${varname+value}

扩展为valueifvarname已设置,否则没有。因此,正在讨论的成语

${1+"$@"}

意味着完全,与简单的完全相同

"$@"

没有那个古老的,极其奇怪的虫子。

所以现在的问题是:什么贝壳这个错误?是否有任何外壳附带任何包含它的甚至模糊的最新操作系统?

--
-贝内特
bet@mordor.com
http://www.mordor.com/bet/
于 2010-02-22T14:50:40.400 回答