解释Perl“序言”的狡猾

时间:2010-02-22 04:50:10

标签: perl shell csh

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;

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

2 个答案:

答案 0 :(得分:27)

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

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

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

由于脚本刚刚启动,$running_under_some_shellundef,这是假的,并且永远不会执行。这是一个无操作。

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

因此,在sh中,$?0表示eval '(exit $?0)',而在csh中表示eval '(exit 00)'。 parens表示应该在子shell中评估exit命令。

sh和csh都将eval '(exit 1)'理解为“执行上一个命令,然后仅在前一个命令退出0时才执行以下命令”。所以只有sh才会执行&&。 csh将进入下一行。

csh会在行的开头忽略“&”。 (我不确定这对csh意味着什么。它的目的是从Perl的角度来看这个表达式。)然后csh继续评估eval 'exec perl -wS $0 ${1+"$@"}'

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

它使用$argv:q代替更常见的${1+"$@"}来解决某些古老版Bourne shell中的错误。他们的意思是一样的。您可以在Bennett Todd's explanation中阅读详细信息(复制在gbacon的答案中)。

答案 1 :(得分:10)

来自Tom Christiansen的收藏Far More Than Everything You've Ever Wanted to Know About …

Why we use eval 'exec perl $0 -S ${1+"$@"}'

  

新闻组:comp.lang.tcl,comp.unix.shell
  来自:bet@ritz.mordor.com(Bennett Todd)
  主题:回复:"$@"${1+"$@"}
  Followup-To:comp.unix.shell
  日期:1995年9月26日星期二,格林威治标准时间14:35:45   消息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}
     如果设置了value,则

扩展为varname,否则不会扩展为${1+"$@"} 。就这样   正在讨论的成语

"$@"
     

与完整的

完全相同
{{1}}
     没有那个古老的,非常奇怪的错误。

     

所以现在问题是:有哪些那个bug?有没有贝壳   包含它的任何甚至是含糊不清的最新操作系统都附带了吗?

-- 
-Bennett
bet@mordor.com
http://www.mordor.com/bet/