在回答这个关于与空间(和潜在的其他字符)的文件名的安全逃逸的问题,其中一个答案说使用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.
Run Code Online (Sandbox Code Playgroud)
在quotemeta的文档,其使用的唯一一提的是逃避所有以外的字符/[A-Za-z_0-9]/
用\
在正则表达式中使用.它没有说明文件名的用途.然而,这似乎是一种非常令人愉快的,如果没有记录的副作用.
在对SinanÜnür 回答早期问题的评论中,霍布斯说:
壳逸出是从正则表达式逃逸不同,虽然我不能想出的情况下quotemeta会给一个真正不安全的结果,它并不意味着任务.如果你必须逃避,而不是绕过shell,我建议尝试String :: ShellQuote采用更加保守的方法,使用sh单引号来除去单引号本身和单引号的反斜杠之外的所有内容. - 霍布斯于2009年8月13日14:25
是否安全 - 完全 - 使用quotemeta代替像String :: Shellquote这样更保守的文件引用?quotemeta utf8或多字节字符安全吗?
我把一个不明确的测试放在一起.除了带有a \n
或其中的文件名或目录名之外,quotemeta似乎运行良好\r
.虽然很少见,但这些字符在Unix中是合法的,我已经看过了.回想一下,LF,CR和NUL等某些字符无法转义\
.我用带有quotemeta的700k文件读取了我的硬盘,没有出现任何故障.
我怀疑(虽然我还没有证明),quotemeta可能会因多字节字符而失败,其中一个或多个字节属于ASCII范围.例如,à
可以编码为一个字符(UTF8 C3 A0)或两个字符(U + 0061表示a
u + 0300是组合格雷夫口音).我在quotemeta中遇到的唯一失败是文件带有\n
或者\r
在我创建的路径中.我会对其他角色感兴趣nasty_names
进行测试.
除了在创建文件时由NUL终止的文件名外,ShellQuote完美地适用于所有文件名.我从来没有遇到过它.
那么用什么?需要明确的是:shell引用不是我经常做的事情,因为我通常只是使用Perl打开来打开进程的管道.该方法不会遇到讨论的shell问题.我感兴趣,因为我看到通常用于文件名转义的quotemeta.
(感谢Ether我添加了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";
Run Code Online (Sandbox Code Playgroud)
hob*_*bbs 15
Quotemeta在这些假设下是安全的:
无论您使用什么引用上下文,shell都违反规则2和3 - 在引号之外,反斜杠 - 换行不会生成换行符; 在双引号中,反斜杠标点符号在输出中添加反斜杠(在某个标点符号列表之外); 在单引号中,一切都是文字的,反斜杠甚至不能保护你免受结束单引号的影响.
String::ShellQuote
如果你需要引用shell的东西,我仍然建议你.我也建议避免让shell进程完全是你的文件名,如果可以的话,通过使用LIST
-form system
/ exec
/ open
或IPC :: Open2,IPC :: Open3,或IPC ::系统::简单.
至于shell之外的东西......很多不同的东西违反了一个或多个规则.例如,过时的POSIX"基本"正则表达式和各种编辑器正则表达式都有标点字符,默认情况下是非特殊的,但在前面加反斜杠时会变得特殊.基本上我所说的是,知道你正在非常好地提供数据,并正确逃脱.只有quotemeta
在它完全合适时使用,或者如果你将它用于不太重要的东西.