Perl如何避免shebang循环?

时间:2016-06-27 17:26:20

标签: perl

perl解释shebang本身并模仿exec*(2)的行为。我认为它模仿了Linux在所有空白上分裂的行为,而不是BSD第一个只有空白的东西,但不要小心。

快速演示really_python.pl

#!/usr/bin/env python

# the following line is correct Python but not correct Perl
from collections import namedtuple
print "hi"

在调用perl really_python.pl时打印hi。

此外,无论是perl program还是./program,以下程序都会做正确的事。

#!/usr/bin/perl
print "hi\n";

#!/usr/bin/env perl
print "hi\n";

我不明白为什么程序没有无限循环。在上述任何一种情况下,shebang行都是或解析为perl解释器的绝对路径。似乎接下来应该发生的事情是perl解析文件,注意到shebang,并委托shebang路径(在本例中为自己)。 perl是否将shebang路径与其自己的ARGV[0]进行比较? perl是否会查看shebang字符串并查看它是否包含"perl"作为子字符串?

我尝试使用符号链接来触发我期待的无限循环行为。

$ ln -s /usr/bin/perl /tmp/p

#!/tmp/p
print "hi\n";

但该程序打印了#34; hi"不论它是如何被调用的。

然而,在OS X上,我能够使用脚本将perl欺骗为无限的shebang循环。

/tmp/pscript

的内容
#!/bin/sh
perl "$@"

perl脚本的内容

#!/tmp/pscript
print "hi\n";

这确实是无限循环(在OS X上,尚未在Linux上测试过它)。

perl在合理的情况下正确处理shebang显然会遇到很多麻烦。它不会被符号链接混淆,也不会被正常的env内容混淆。到底是做什么的?

2 个答案:

答案 0 :(得分:12)

此功能的文档位于perlrun

  

如果#!行不包含单词“perl”或单词“indir”,则执行以#!命名的程序而不是Perl解释器。这有点奇怪,但它可以帮助那些没有#!的机器上的人,因为他们可以告诉程序他们的SHELL是 / usr / bin / perl ,然后Perl会将程序发送给正确的解释器。

因此,如果shebang包含perlindir,则不会执行shebang行的解释器。

Additionally,如果argv[0]不包含perl,则不执行shebang行的解释程序。这就是阻止示例中无限循环的原因。

  • 使用perl /tmp/pscript启动时,

    1. 内核执行perl /tmp/pscript
    2. 然后perl执行/tmp/p /tmp/pscript
    3. 此时,argv[0]不包含perl,因此shebang行不再相关。
  • 使用/tmp/pscript启动时,

    1. 内核执行/tmp/p /tmp/pscript
    2. 此时,argv[0]不包含perl,因此shebang行不再相关。

答案 1 :(得分:12)

相关代码位于toke.c,即Perl词法分析器。如果:

  • 第1行以#!开头(可选地以空格开头)AND

  • 不包含perl - AND

  • 不包含perl(除非后跟6,即perl6)AND

  • (在" DOSish"平台上)不包含perl的不区分大小写的匹配(例如Perl)和

  • 不包含indir AND

  • 未在命令行AND

  • 上设置-c标志
  • argv[0]包含perl

使用execv执行shebang之后的程序。否则,词法分析器就会继续; perl并不是exec本身。

结果,你可以用shebang做一些非常奇怪的事情而不用perl试图exec另一个翻译:

    #!     perl
#!foo perl
#!fooperlbar -p
#!perl 6
#!PeRl          # on Windows

您的符号链接示例符合上面列出的所有条件,那么为什么没有无限循环?你可以看到strace

正在发生什么
$ ln -s /usr/bin/perl foo
$ echo '#!foo' > bar
$ strace perl bar 2>&1 | grep exec
execve("/bin/perl", ["perl", "bar"], [/* 27 vars */]) = 0
execve("foo", ["foo", "bar"], [/* 27 vars */]) = 0

Perl实际上会exec链接,但由于它在名称中不包含perl,因此第二次不再满足最后一个条件并且循环结束。