Perl:quotemeta是否仅用于正则表达式?文件名安全吗?
在回答关于使用空格(以及可能的其他字符)安全转义文件名的问题时,据说使用了Perl内置的函数 quotemeta的文件说明:Perl:quotemeta是否仅用于正则表达式?文件名安全吗?,perl,file,unix,Perl,File,Unix,在回答关于使用空格(以及可能的其他字符)安全转义文件名的问题时,据说使用了Perl内置的函数 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. 在
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.
在quotemeta的文档中,唯一提到它的用法是在正则表达式中使用\
对/[A-Za-z_0-9]/
以外的所有字符进行转义。它没有说明文件名的用途。然而,这似乎是一个非常令人愉快的副作用,如果没有记录在案的话
在对SinanÜnür先前问题的评论中,霍布斯指出:
外壳逸出不同于
regexp正在逃跑,虽然我不能
想出一个情况
quotemeta会给出一个真正不安全的答案
结果,它不适合任务。
如果你必须逃跑,而不是
绕过外壳,我建议试试
字符串::ShellQuote,它需要更多
使用sh单参数的保守方法
引用来破坏一切,除了
单引号本身,以及
单引号的反斜杠霍布斯
2009年8月13日14:25
使用quotemeta代替像这样更保守的文件引用安全吗?quotemeta utf8或多字节字符安全吗
我做了一个不清楚的测试。quotemeta似乎工作得很好,除了文件名或目录名中带有\n
或\r
。虽然很少见,但这些字符在Unix中是合法的,我见过它们。回想一下,某些字符,如LF、CR和NUL不能用\
转义。我用quotemeta读取硬盘上的700k文件,没有出现任何故障
我怀疑(尽管我还没有演示)quotemeta可能会在一个或多个字节属于ASCII范围的多字节字符中失败。例如,a
可以编码为一个字符(UTF8 C3 A0)或两个字符(U+0061表示a
U+0300表示组合graves重音)。我对quotemeta的唯一失败是我创建的路径中有\n
或\r
的文件。我对放入讨厌的\u名称
进行测试的其他字符感兴趣
在创建文件时,除以NUL结尾的文件名外,其他文件名都可以完美地使用。我从来没有失败过
那么使用什么呢?需要明确的是:shell引用不是我经常做的事情,因为我通常只使用perlopen来打开一个进程的管道。这种方法不受讨论的shell问题的影响。我很感兴趣,因为我看到quotemeta经常用于文件名转义
(多亏了乙醚,我添加了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";
您还可以使用capture()
或capturex()
(我在关于第一个问题的另一个答案中建议了这一点),这样可以绕过shell
我将这些行添加到脚本中,发现没有失败的示例:
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
专门用于转义正则表达式中的重要字符,正如您所发现的,这些字符与shell中重要的字符集不是完全重叠的。quotemeta在以下假设下是安全的:
String::ShellQuote
。我还建议避免让shell完全处理您的文件名,如果可以的话,可以使用LIST
-formsystem
/exec
/open
或
至于外壳以外的东西。。。许多不同的事情违反了一条或多条规则。例如,过时的POSIX“basic”正则表达式和各种编辑器正则表达式都有标点符号,默认情况下标点符号不特殊,但在前面加反斜杠时会变得特殊。基本上,我想说的是,要非常清楚地知道你要将数据输入的对象,并正确地逃离。仅在完全适合的情况下使用quotemeta
,或者在不太重要的情况下使用它。以下是一种仅适用于Unix的解决方案;有关Windows支持,请参阅
另一种选择是这个简单的函数,即使使用非ASCII字符(假设编码正确),它也能稳定地工作,\n
和\r
,但不包括NUL
(见底部)
函数用单引号括住每个参数,如果指定了多个参数,则用空格分隔
之所以使用单引号字符串,是因为它们的内容在POSIX类shell中不受任何解释的约束
但是,同样地,您甚至不能转义“
实例本身,这需要以下解决方法:每个嵌入的”
实例都替换为“\''
(sic),这将有效地将输入字符串拆分为多个单引号字符串,将转义的'
实例-\'
-拼接到壳中,然后将字符串部分重新组装成单个字符串
例如:
print quoteforsh 'I\'m here & wëll';
从字面上产生(包括随附的单引号)'I'\'m here&wëll'
,它
print quoteforsh 'I\'m here & wëll';
system "echo 'a\x{0}b'"; # BREAKS