有人可以解释这个使用 sh 然后执行 perl 的 shebang 行吗?

6 shell scripting perl

这是来自Programming Perl,第四版。这是关于执行 Perl 脚本。

最后,如果你不幸在一个不支持魔法 #! 行,或者如果您的解释器的路径超过 32 个字符(许多系统的内置限制),您可以像这样解决它:

#!/bin/sh -- # perl, to stop looping
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
if 0;
Run Code Online (Sandbox Code Playgroud)

你能一步一步解释这里发生了什么吗?我试图让它工作或包含,但无济于事。

在执行上述操作时,我得到了这个:

/bin/sh: 0: Illegal option --
Run Code Online (Sandbox Code Playgroud)

ilk*_*chu 8

这个想法是eval命令在 shell 和 Perl 中都有效,区别在于换行符在 shell 中终止命令,而不是在 Perl 中。相反,Perl 读取以下行,其中添加了条件if 0,有效地否定了整个命令。

(Perl 支持一些这样的“向后”结构的速记,例如,您可以编写next if $_ == 0代替if ($_ == 0) { next }print for @a代替for (@a) { print }。)

如果脚本是由 shell 启动的,shell 会处理eval, 并用 Perl 解释器替换它,给它脚本名称 ( $0) 和它的参数 ( $@) 作为参数。

然后 Perl 运行,读取eval,跳过它(因为if 0),然后继续执行脚本的其余部分。


这就是它应该如何工作。在实践中,您会因为两件事而得到错误:1) Perl 读取 hashbang 行本身,以及 2) Linux 处理 hashbang 行的方式。

当 Perl 运行带有 hashbang 的脚本时,它并不会真正将其视为注释。相反,它会解释 hashbang 中给出的 Perl 的任何选项(你可以有#!/usr/bin/perl -Wln等),但它也会检查解释器并在脚本不应该由 Perl 运行时执行它!

试试这个:

$ cat > hello.sh
#!/bin/bash
echo $BASH_VERSION 
$ perl hello.sh
4.4.12(1)-release
Run Code Online (Sandbox Code Playgroud)

这实际上运行 Bash。

所以,注释#perl是告诉 Perl 是的,这实际上应该由 Perl 运行,这样它就不会再次启动 shell 。

但是,Linux 将解释器名称后面的所有内容作为单个参数,因此当您运行脚本时,它会运行/bin/sh,并带有两个参数-- # perl, to stop looping, 和scriptname.pl。第一个以破折号开头,但不完全是--,因此 Dash 和 Bash 都尝试将其解释为选项。它不是一个有效的,所以你会得到一个错误,就像你试图运行bash ---, 或bash "-- #perl".

在将 hashbang 行中的参数分开的其他系统上,-- #perl会给 shell --, #perl,并且它会尝试查找名为#perl. 这又行不通了。但显然有/已经有一些系统将#符号作为行中的注释标记#!,和/或只传递第一个参数。(有关此事,请参阅Sven Mascheck 的页面。)在这些系统上,它可能会起作用。


perlrun(改编)中给出了一个更好的工作:

#!/usr/bin/perl
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if $running_under_some_shell;
print("Perl $^V\n");
Run Code Online (Sandbox Code Playgroud)

运行它bash ./script.pl实际上运行 Perl 并打印 (eg) Perl v5.24.1。($running_under_some_shell只是一个未定义的变量,默认为假。if 0会更清晰但没有描述性。)


就像您的报价中所说的那样,所有这些仅在#!无法正常工作的古代系统上才需要。在某些情况下,它根本不受支持,并且要求 shell 运行非二进制文件总是在 shell 中运行它。在其他情况下,hashbang 行中的路径长度有限制,因此#!/really/veeeery/long/path/to/perl行不通。

只需将#!/usr/bin/perlhashbang 放在任何现代系统上即可。