Perl: Является ли цитата только для регулярных выражений? Безопасно ли это для имен файлов?

Отвечая на этот вопрос о безопасном экранировании имени файла пробелами (и, возможно, другими символами), в одном из ответов говорилось, что используется встроенный 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";

8
задан Community 23 May 2017 в 11:53
поделиться