6

我知道这是不正确的。我只想知道 perl 是如何解析这个的。

所以,我在玩 perl,我想要的是perl -ne我输入perl -ie的行为有点有趣,我想知道发生了什么。

$ echo 1 | perl -ie'next unless /g/i'

所以 perl Aborted (core dumped)perl --help我看到的阅读-i需要扩展备份。

-i[extension]     edit <> files in place (makes backup if extension supplied)

对于那些不知道的人-e来说只是评估。所以我在想三件事中的一件可能会发生,或者它被解析为

  1. perl -i -e'next unless /g/i' 我得到undef,其余的作为e的参数
  2. perl -ie 'next unless /g/i' 我得到参数e,其余的像文件名一样挂起
  3. perl -i"-e'next unless /g/i'" 整件事作为我的论据

当我跑

$ echo 1 | perl -i -e'next unless /g/i'

程序不会中止。这使我相信它'next unless /g/i'没有被解析为-e. 毫无疑问,上面的内容会以这种方式进行解析,并且会产生不同的结果。

那是什么?好吧,再玩一点,我得到了

$ echo 1 | perl -ie'foo bar'
Unrecognized switch: -bar  (-h will show valid options).

$ echo 1 | perl -ie'foo w w w'
... works fine guess it reads it as `perl -ie'foo' -w -w -w`

玩弄上面的,我试试这个......

$ echo 1 | perl -ie'foo e eval q[warn "bar"]'
bar at (eval 1) line 1.

现在我真的很困惑.. 那么 Perl 是如何解析这个的呢?最后,似乎您实际上可以从 just 中获取 Perl eval 命令-i。这有安全隐患吗?

$ perl -i'foo e eval "warn q[bar]" '
4

2 回答 2

7

快速回答

壳牌报价处理正在折叠和连接它认为只是一个论点的东西。您的调用相当于

$ perl '-ienext unless /g/i'

它立即中止,因为 perl 将此参数解析为包含-u,这会触发核心转储,您的代码将开始执行。这是一个曾经用于创建伪可执行文件的旧功能,但如今它在本质上已经退化了。

似乎eval是对-e 'ss /g/i'.

第一条线索

B::Deparse可以你的朋友,只要你碰巧在没有dump支持的系统上运行。

$ echo 1 | perl -MO=Deparse,-p -ie'next unless /g/i'
dump is not supported.
BEGIN { $^I = "enext"; }
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
    chomp($_);
    (('ss' / 'g') / 'i');
}

那么为什么会unle消失呢?如果您正在运行 Linux,那么您可能还没有达到我的水平。上面的输出来自 Cygwin 上的 Perl,关于dump不受支持的错误是一个线索。

下一条线索

perlrun 文档中的注意事项:

-u

此开关会导致 Perl 在编译程序后转储内核。然后,理论上您可以使用undump程序(未提供)将此核心转储并转换为可执行文件。这会以牺牲一些磁盘空间为代价来加快启动速度(您可以通过剥离可执行文件来最小化)。(不过,“hello world”可执行文件在我的机器上大约有 200K。)如果您想在转储之前执行程序的一部分,请改用dump运算符。注意:undump的可用性是平台特定的,可能不适用于特定的 Perl 端口。

工作假设和确认

Perl 的参数处理将整个块视为单个选项集群,因为它以破折号开头。该-i选项使用下一个单词 ( enext),正如我们在处理的实现中-i看到的那样。

case 'i':
    Safefree(PL_inplace);
    [Cygwin-specific code elided -geb]
    {
        const char * const start = ++s;
        while (*s && !isSPACE(*s))
            ++s;

        PL_inplace = savepvn(start, s - start);
    }
    if (*s) {
        ++s;
        if (*s == '-')      /* Additional switches on #! line. */
            s++;
    }
    return s;

对于备份文件的扩展名,上面来自perl.c的代码最多使用第一个空白字符或字符串结尾,以先到者为准。如果还有字符,第一个必须是空格,然后跳过它,如果下一个是破折号,那么也跳过它。在 Perl 中,您可以将此逻辑写为

if ($$s =~ s/i(\S+)(?:\s-)//) {
  my $extension = $1;
  return $extension;
}

然后,所有-u, -n, -l, 和-e都是有效的 Perl 选项,因此参数处理会吃掉它们并留下无意义的

ss /g/i

作为 的参数-e,perl 将其解析为一系列除法。但在执行甚至可以开始之前,古老的-u导致 perl 转储核心。

意外行为

更奇怪的是,如果你在next和之间放置两个空格unless

$ perl -ie'next  unless /g/i'

程序尝试运行。回到我们看到的主选项处理循环

case '*':
case ' ':
    while( *s == ' ' )
      ++s;
    if (s[0] == '-')        /* Additional switches on #! line. */
        return s+1;
    break;

额外的空格会终止对该参数的选项解析。见证:

$ perl -ie'next 废话 -garbage --foo' -e die
死于 -e 第 1 行。

但没有我们看到的额外空间

$ perl -ie'next 废话 -garbage --foo' -e die
无法识别的开关:-onsense -garbage --foo(-h 将显示有效选项)。

然而,有了额外的空格和破折号,

$ perl -ie'next -除非 /g/i'
不支持转储。

设计动机

正如评论所表明的那样,为了严格的 shebang( #!) 行约束而存在逻辑,perl 会尽力解决这个问题。

解释器脚本

解释器脚本是一个启用了执行权限的文本文件,其第一行的格式为:

#! interpreter [optional-arg]

解释器必须是本身不是脚本的可执行文件的有效路径名。如果 filename 参数execve指定了解释器脚本,则解释器将使用以下参数调用:

interpreter [optional-arg] filename arg...

其中arg...argv是 的论点所指向的一系列词execve

对于便携使用,optional-arg应该不存在,或者指定为单个单词(,它不应包含空格)......</p>

于 2012-05-22T20:27:06.353 回答
5

需要知道的三件事:

  1. '-x y'表示-xyPerl(对于一些任意选项“x”和“y”)。

  2. -xy与 unix 工具一样,是一个代表-x -y.

  3. -i, like-e吸收了其余的论点。与 不同-e,它认为空格是参数的结尾(根据上面的#1)。

这意味着

-ie'next unless /g/i'

这只是一种奇特的写作方式

'-ienext unless /g/i'

解绑到

-ienext -u -n -l '-ess /g/i'
  ^^^^^             ^^^^^^^
----------         ----------
val for -i         val for -e

perlrun文件-u为:

此开关会导致 Perl 在编译程序后转储内核。然后,理论上您可以使用 undump 程序(未提供)将此核心转储并转换为可执行文件。这会以牺牲一些磁盘空间为代价来加快启动速度(您可以通过剥离可执行文件来最小化)。(不过,“hello world”可执行文件在我的机器上大约有 200K。)如果您想在转储之前执行程序的一部分,请改用 dump() 运算符。注意:undump 的可用性是平台特定的,可能不适用于特定的 Perl 端口。
于 2012-05-22T21:03:49.680 回答