При ответе на на этот вопрос, касающийся безопасного экранирования имени файла с пробелами (и, возможно, другими символами), один из ответов , который, как говорят, использует встроенный в Perl quotemeta function.
Документация о 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 единственное упоминание о ее использовании - экранировать все символы, кроме/[A-Za-z_0-9]/
с \
для использования в регулярном выражении.В нем не указано использование имен файлов.Однако это кажется очень приятным, хотя и недокументированным, побочным эффектом.
В комментарии к Синан Юнюр ответ на предыдущий вопрос Хоббс утверждает:
экранирование оболочки отличается от экранирования регулярного выражения,и хотя я не могу придумать ситуацию, когда quotemeta даст действительно небезопасный результат, он не предназначен для этой задачи.Если вы должны уйти, вместо обхода оболочки, я предлагаю попробовать String :: ShellQuote, который использует более консервативный подход, используя sh одинарные кавычки, чтобы очистить все, кроме самих одинарных кавычек, и обратную косую черту для одинарных кавычек.- Хоббс 13 августа 2009 года в 14: 25
Безопасно ли - полностью - использовать quotemeta вместо более консервативного цитирования файлов, такого как String :: Shellquote ?Является ли quotemeta utf8 или многобайтовый символ безопасным?
Я собрал непонятный тест.Похоже, quotemeta работает хорошо, за исключением имени файла или имени каталога с \n
или \r
в нем.Хотя эти символы встречаются редко, они являются законными в Unix, и я их видел.Напомним, что некоторые символы, такие как LF, CR и NUL, нельзя экранировать с помощью \
.Я прочитал мой жесткий диск с 700k файлами с quotemeta и не имел сбоев.
У меня есть подозрение (хотя я еще не продемонстрировал это), что quotemeta может завершиться ошибкой с многобайтовыми символами, когда один или несколько байтов попадают вДиапазон ASCII.Например, à
может быть закодирован как один символ (UTF8 C3 A0) или как два символа (U + 0061 дает a
u + 0300 - акцент сочетания могил).Единственный продемонстрированный сбой, который я имею с quotemeta - это файлы с \n
или \r
в пути, который я создал.Мне было бы интересно, чтобы другие символы добавили nasty_names
для проверки.
ShellQuote отлично работает со всеми именами файлов, кроме тех, которые заканчиваются NUL при создании файла.У меня никогда не было с этим проблем.
Так что же использовать?Просто чтобы прояснить: цитирование оболочки - это не то, чем я часто занимаюсь, поскольку я обычно использую Perl open, чтобы открыть канал для процесса.Этот метод не страдает от обсуждаемых проблем оболочки.Я заинтересован, так как я видел, что 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";