Perl模糊的命令行选项,以及使用-i的eval的安全含义?

时间:2012-05-22 19:50:41

标签: perl command-line-arguments

我知道这是不正确的。我只是想知道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只是eval的人。所以我认为在解析为

之后,可能发生了三件事之一
  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'" 整个事情作为我
  4. 的论据

    当我跑步时

    $ 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如何解析这个呢?最后,您似乎可以从-i内获得Perl eval命令。这有安全隐患吗?

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

2 个答案:

答案 0 :(得分:7)

快速回答

Shell报价处理正在崩溃并连接它认为是一个参数的内容。您的调用等同于

$ 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 documentation

的注释
  

<强> -u

     

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

工作假设和确认

Perl的参数处理将整个块视为一个选项集群,因为它以破折号开头。我们可以在implementation for -i processing中看到-i选项使用下一个字(enext)。

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转储核心。

意外行为

如果你在nextunless

之间放置两个空格,那就更奇怪了
$ perl -ie'next  unless /g/i'

程序试图运行。 Back in the main option-processing loop我们看到了

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

额外空间终止对该参数进行解析的选项。证人:

$ perl -ie'next  nonsense -garbage --foo' -e die
Died at -e line 1.

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

$ perl -ie'next nonsense -garbage --foo' -e die
Unrecognized switch: -onsense -garbage --foo  (-h will show valid options).

但是,如果有额外的空格和破折号,

$ perl -ie'next  -unless /g/i'
dump is not supported.

设计动机

正如评论所指出的那样,逻辑就是harsh shebang (#!) line constraints,perl会尽力解决这个问题。

  

解释器脚本

     

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

#! interpreter [optional-arg]
     

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

interpreter [optional-arg] filename arg...
     

其中 arg ... argv的{​​{1}}参数指向的一系列单词。

     

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

答案 1 :(得分:5)

要了解三件事:

  1. '-x y'表示-xy为Perl(对于某些任意选项“x”和“y”)。

  2. 与liix工具相同的
  3. -xy是代表-x -y的“捆绑”。

  4. -i,就像-e吸收其余的论点一样。与-e不同,它将空格视为参数的结尾(按照上面的#1)。

  5. 这意味着

    -ie'next unless /g/i'
    

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

    '-ienext unless /g/i'
    

    unbundles to

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

    perlrun个文件-u为:

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