代码之家  ›  专栏  ›  技术社区  ›  dawg

Perl:quotemeta只用于正则表达式吗?文件名安全吗?

  •  8
  • dawg  · 技术社区  · 14 年前

    回答时 this 关于用空格(以及可能的其他字符)安全转义文件名的问题, one of the answers 据说使用Perl的内置 quotemeta

    quotemeta的文件规定:

    quotemeta (and \Q ... \E ) are useful when interpolating strings 
    into regular expressions, because by default an interpolated variable 
    will be considered a mini-regular expression.  
    

    /[A-Za-z_0-9]/ 用一个 \

    在对思南nr的评论中 answer

    空壳逸出 regexp正在逃跑,尽管我不能 提出一种情况 quotemeta会给你一个真正不安全的 如果你必须逃跑,而不是 绕过外壳,我建议试试 String::ShellQuote,它需要更多的 引用来诋毁一切除了 2009年8月13日14:25

    使用quotemeta代替像这样更保守的文件引用是安全的吗 String::Shellquote ? quotemeta utf8或多字节字符安全吗?

    \n ,或 \r \

    Ã 可以编码为一个字符(UTF8 C3 A0)或两个字符(U+0061) a \不 \右 nasty_names 测试。

    ShellQuote 在创建文件时,除了以NUL结尾的文件名之外,其他文件名都能正常工作。我从来没有失败过。

    (多亏了乙醚,我添加了IPC::System::Simple)

    测试文件:

    use strict; use warnings; use autodie;
    use String::ShellQuote;
    use File::Find;
    use File::Path;
    use IPC::System::Simple 'capturex';
    
    my @nasty_names;
    my $top_dir = '/Users/andrew/bin/pipetestdir/testdir';
    my $sub_dir = "easy_to_remove_me";
    my (@qfail, @sfail, @ipcfail);
    
    sub wanted { 
        if ($File::Find::name) { 
             my $rtr;
             my $exec1="ls ".quotemeta($File::Find::name);
             my $exec2="ls ".shell_quote($File::Find::name);
             my @exec3= ("ls", $File::Find::name);
    
             $rtr=`$exec1`;  
             push @qfail, "$exec1" 
                  if $rtr=~/^\s*$/ ;
    
             $rtr=`$exec2`;
             push @sfail, "$exec2" 
                  if $rtr=~/^\s*$/ ;
    
             $rtr = capturex(@exec3);
             push @ipcfail, \@exec3
                  if $rtr=~/^\s*$/ ;     
        }
    }
    
    chdir($top_dir) or die "$!";
    mkdir "$top_dir/$sub_dir";
    chdir "$top_dir/$sub_dir";
    
    push @nasty_names, "name with new line \n in the middle";
    push @nasty_names, "name with CR \r in the middle";
    push @nasty_names, "name with tab\tright there";
    push @nasty_names, "utf \x{0061}\x{0300} combining diacritic";
    push @nasty_names, "utf e̋ alt combining diacritic";
    push @nasty_names, "utf e\x{cc8b} alt combining diacritic";
    push @nasty_names, "utf άέᾄ greek";
    push @nasty_names, 'back\slashes\\Not\\\at\\\\end';
    push @nasty_names, qw|back\slashes\\IS\\\at\\\\end\\\\|;
    
    sub create_nasty_files {
        for my $name (@nasty_names) {
           open my $fh, '>', $name ; 
           close $fh;
        }
    }
    
    for my $dir (@nasty_names) {
        chdir("$top_dir/$sub_dir");
        mkpath($dir);
        chdir $dir;
        create_nasty_files();
    }
    
    find(\&wanted, $top_dir);
    
    print "\nquotemeta failed on:\n", join "\n", @qfail;
    print "\nShell Quote failed on:\n", join "\n", @sfail;
    print "\ncapturex failed on:\n", join "\n", @ipcfail;
    print "\n\n\n",
          "Remove \"$top_dir/$sub_dir\" before running again...\n\n";
    
    3 回复  |  直到 7 年前
        1
  •  15
  •   hobbs    14 年前

    1. 如果一个非字母数字字符有一个特殊的含义,在它前面加一个反斜杠将使它总是非特殊的。
    2. 如果一个非字母数字字符没有特殊的含义,在它前面加反斜杠将毫无用处。

    shell违反了第2条和第3条规则,不管您使用什么引号上下文——在引号之外,反斜杠换行符不生成换行符;在双引号中,反斜杠标点符号将反斜杠放入输出(在某个标点符号列表之外);在单引号中,每件事都是文字和反斜杠甚至不能保护你对一个结束单引号。

    我还是推荐你 String::ShellQuote 如果你需要为壳牌公司报价的话。我还建议避免让shell完全处理文件名,如果可以的话,使用 LIST system / exec / open IPC::Open2 IPC::Open3 ,或 IPC::System::Simple

    至于壳以外的东西。。。很多不同的事情违反了一个或多个规则。例如,过时的POSIX“basic”正则表达式和各种编辑器正则表达式都有标点字符,默认情况下这些字符不是特殊的,但是 变得特别 quotemeta 如果它是一个完全合适的,或者如果你用它来做一些不太重要的事情。

        2
  •  3
  •   Ether    14 年前

    你也可以用 IPC::System::Simple capture() capturex() (我在关于第一个问题的另一个回答中建议了这个问题),这将让你绕过外壳。

    我将这些行添加到脚本中,发现没有失败的示例:

    use IPC::System::Simple 'capturex';
    ...
    my (@qfail, @sfail, @ipcfail);
    ...
             my @exec3= ("ls", $File::Find::name);
    ...
             $rtr = capturex(@exec3);
             push @ipcfail, \@exec3
                  if $rtr=~/^\s*$/ ;
    ...
    print "\ncapturex failed on:\n", join "\n", @ipcfail;
    

    但一般来说,你应该解决实际问题,而不是试图找到更好的创可贴。 quotemeta

        3
  •  0
  •   Community kfsone    7 年前

    以下是一个仅用于Unix的解决方案;请参阅 https://stackoverflow.com/a/32161361/45375 Windows支持。

    \n ,和 \r ,但不包括 NUL (见下图)。

    sub quoteforsh { join ' ', map { "'" . s/'/'\\''/gr . "'" } @_ }
    

    该函数用单引号括住每个参数,如果指定了多个参数,则用空格分隔。

    然而,就这样,你甚至无法逃脱 ' 实例替换为 '\'' (sic),它有效地将输入字符串拆分为多个单引号字符串,并带有转义 ' \' - 拼接 -然后外壳将细绳部分重新组装成一根细绳。

    例子:

    print quoteforsh 'I\'m here & wëll';
    

    字面上产生( 包括 (包括单引号) 'I'\''m here & wëll' 字符串- 'I' , ,和 '&well' 然后外壳重新组装成一个 单一的 I'm here & wëll .


    OSX Unicode警告 Unicode标准格式(基音后跟另一个字符,即相关的音调符号),而Perl通常创建NFC( 组成

    使用时 字面意义的 文件名,这种区别并不重要(系统调用进行映射),但是当使用globs时,它确实重要,而且,不幸的是,您必须自己翻译这两种形式。


    努尔

    我不认为 努尔 查尔斯。在文件名中存在一个现实问题:

    • 最像是贝壳( bash , dash ksh ) 努尔 查尔斯。上 命令行 zsh 是唯一的例外。
    • 即使这不是一个问题,根据 Wikipedia 支持 努尔 查尔斯。在文件名中。

    另外,试图用 努尔 system() 功能 打破 调用,大概是因为字符串传递给 sh -c 切断 一开始 努尔 :

    system "echo 'a\x{0}b'";  # BREAKS