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