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

Eva*_*oll 6 perl command-line-arguments

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

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

$ echo 1 | perl -ie'next unless /g/i'
Run Code Online (Sandbox Code Playgroud)

那就是perl Aborted (core dumped).阅读perl --help我看到-i需要扩展备份.

-i[extension]     edit <> files in place (makes backup if extension supplied)
Run Code Online (Sandbox Code Playgroud)

对于那些不知道-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'" 整件事作为我的论据

我跑的时候

$ echo 1 | perl -i -e'next unless /g/i'
Run Code Online (Sandbox Code Playgroud)

该计划不会中止.这让我相信'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`
Run Code Online (Sandbox Code Playgroud)

玩弄以上,我试试这个......

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

现在我真的很困惑..那么Perl如何解析这个呢?最后,看起来你实际上可以从内部获得一个Perl eval命令-i.这有安全隐患吗?

$ perl -i'foo e eval "warn q[bar]" '
Run Code Online (Sandbox Code Playgroud)

Gre*_*con 7

快速回答

Shell报价处理正在崩溃并连接它认为是一个论点.你的调用相当于

$ perl '-ienext unless /g/i'
Run Code Online (Sandbox Code Playgroud)

它会立即中止,因为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');
}
Run Code Online (Sandbox Code Playgroud)

那么为什么会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;
Run Code Online (Sandbox Code Playgroud)

对于备份文件的扩展名,perl.c上面的代码消耗最多的第一个空格字符或字符串结尾,以先到者为准.如果字符仍然存在,则第一个必须是空格,然后跳过它,如果下一个是破折号,则跳过它.在Perl中,您可以将此逻辑编写为

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

然后,所有的-u,-n,-l,和-e有效的Perl选项,因此参数处理吃掉他们,离开无意义

ss /g/i
Run Code Online (Sandbox Code Playgroud)

作为参数-e,perl解析为一系列分歧.但是在执行甚至开始之前,古老的-u原因导致perl转储核心.

意外的行为

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

$ perl -ie'next  unless /g/i'
Run Code Online (Sandbox Code Playgroud)

程序试图运行.回到主选项处理循环,我们看到了

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

额外空间终止对该参数的选项解析.见证人:

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

设计动机

正如评论所指出的那样,逻辑是出于严格的shebang(#!)行约束,perl尽力解决.

解释器脚本

解释器脚本是一个启用了执行权限且第一行具有以下形式的文本文件:

#! interpreter [optional-arg]
Run Code Online (Sandbox Code Playgroud)

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

interpreter [optional-arg] filename arg...
Run Code Online (Sandbox Code Playgroud)

其中arg ...是由argv参数指向的一系列单词execve.

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

  • @Greg Bacon,我不认为Linux是一个古老的系统. (3认同)

ike*_*ami 5

要知道的三件事:

  1. '-x y'意味着-xyPerl(对于某些任意选项"x"和"y").

  2. -xy,就像unix工具一样,是一个"捆绑"代表-x -y.

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

这意味着

-ie'next unless /g/i'
Run Code Online (Sandbox Code Playgroud)

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

'-ienext unless /g/i'
Run Code Online (Sandbox Code Playgroud)

unbundles to

-ienext -u -n -l '-ess /g/i'
  ^^^^^             ^^^^^^^
----------         ----------
val for -i         val for -e
Run Code Online (Sandbox Code Playgroud)

perlrun文件-u为:

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