解释Perl"序言"的狡猾

Joe*_*Fan 14 shell perl 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;
Run Code Online (Sandbox Code Playgroud)

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

cjm*_*cjm 27

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

在Perl中,三条线形成一个语句,由终止;,表单的

eval '...' && eval '...' & eval '...' if $running_under_some_shell;
Run Code Online (Sandbox Code Playgroud)

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

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

因此,在sh,eval '(exit $?0)'means eval '(exit 00)'和csh中它意味着eval '(exit 1)'.parens表示应该在子shell中评估exit命令.

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表示与-w(启用警告)和-S(查找指定的脚本$PATH)相同. $0是脚本的文件名.最后两个${1+"$@"}$argv:q生成当前命令行参数的副本(分别在sh和csh中).

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


Gre*_*con 10

从汤姆克里斯蒂安森的收藏品远远超过你曾经想知道的一切......:

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

新闻组:comp.lang.tcl,comp.unix.shell
来自:bet@ritz.mordor.com(Bennett Todd)
主题:Re:"$@"${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
Run Code Online (Sandbox Code Playgroud)

所以当你添加周围的引号时,它就完整地引用了整个schmeer.可爱,可爱,太可爱......现在考虑一下正确的用法

"$@"
Run Code Online (Sandbox Code Playgroud)

如果没有args,将扩展为:

""
Run Code Online (Sandbox Code Playgroud)

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

${varname+value}
Run Code Online (Sandbox Code Playgroud)

扩展为valueif varname设置,否则没有.因此,正在讨论的成语

${1+"$@"}
Run Code Online (Sandbox Code Playgroud)

意思是指与一个简单的完全相同

"$@"
Run Code Online (Sandbox Code Playgroud)

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

那么现在的问题是:那个虫什么炮弹?是否包含任何包含它的最近操作系统附带的任何外壳?

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