Perl: есть ли quotemeta только для регулярных выражений?Это безопасно для имен файлов? - PullRequest
8 голосов
/ 26 сентября 2010

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

Ответы [ 3 ]

15 голосов
/ 26 сентября 2010

Quotemeta безопасен при следующих допущениях:

  1. Специальное значение имеют только не алфавитно-цифровые символы.
  2. Если не алфавитно-цифровой символ имеет специальное значение, добавление обратной косой черты вперед ним всегда будет ничего особенного.
  3. Если не алфавитно-цифровой символ не имеет специального значения, то добавление обратной косой черты перед ним ничего не изменит.

Оболочка нарушает правила 2 и 3 независимо от того, какой контекст цитаты вы используете - вне кавычек обратный слэш-новая строка не генерирует новую строку;в двойных кавычках пунктуация с обратной косой чертой помещает обратную косую черту в вывод (за пределами определенного списка знаков препинания);а в одинарных кавычках все буквально, а обратный слеш даже не защищает вас от закрывающей одинарной кавычки.

Я все еще рекомендую String::ShellQuote, если вам нужно что-то заключить в оболочку.Я также рекомендую избегать, чтобы оболочка полностью обрабатывала ваши имена файлов, используя LIST -form system / exec / open или IPC :: Open2 , IPC:: Open3 , или IPC :: System :: Simple .

Что касается вещей, кроме оболочки ... множество разных вещей нарушают одно или несколько правил.Например, устаревшие «базовые» регулярные выражения POSIX и различные типы регулярных выражений редактора имеют знаки препинания, которые по умолчанию не являются специальными, но становятся специальными , когда им предшествует обратная косая черта.По сути, я говорю: знайте, что вы очень хорошо кормите свои данные, и правильно избегайте.Используйте quotemeta только в том случае, если это точно подходит, или если вы используете его для чего-то, что не очень важно.

3 голосов
/ 26 сентября 2010

Вы также можете использовать IPC :: System :: Simple capture() или capturex() (что я предложил в другом ответе на этот первый вопрос), что позволит вам обойти оболочку.

Я добавил эти строки в ваш скрипт и обнаружил, что ни одного примера не удалось:

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 предназначен специально для экранирования значимых для регулярного выражения символов, которые, как вы обнаружили, не полностью совпадают с набором значимых для оболочки символов.

0 голосов
/ 25 августа 2015

Ниже приводится решение только для Unix;см. https://stackoverflow.com/a/32161361/45375 для поддержки Windows.

Альтернативой является эта простая функция, которая должна работать надежно даже с не-ASCII-символами (при условии правильной кодировки), а также \nи \r, но исключая NUL (см. внизу).

sub quoteforsh { join ' ', map { "'" . s/'/'\\''/gr . "'" } @_ }

Функция заключает каждый аргумент в одинарные кавычки и, если указано несколько аргументов, разделяет их пробелами.

Строки в одинарных кавычках используются, поскольку их содержимое не подлежит какой-либо интерпретации в POSIX-подобных оболочках.

Тем не менее, вы не можете даже избежать ' самих экземпляров, что требует следующегоОбходной путь: каждый встроенный экземпляр ' заменяется на '\'' (sic), который эффективно разбивает входную строку на несколько строк в одинарных кавычках, с экранированными ' экземплярами - \' - , сращенными в - оболочка затем собирает части строки в одну строку.

Пример:

print quoteforsh 'I\'m here & wëll';

буквально производит ( включая включающие одинарные кавычки) 'I'\''m here & wëll', которые для оболочки представляют собой 3 смежных строк - 'I', \' и '&well', которые затемсобирает в одну строку, которая после удаления кавычек дает I'm here & wëll.


OSX Unicode caveat : HFS + сохраняет имена файлов в NFD ( разлагается нормальная форма Unicode - базовая буква, за которой следует другой символ, связанный с диакритическим знаком), тогда как Perl обычно создает NFC ( составленная нормальная форма Unicode - один символ идентифицирует букву с акцентом).

При использовании буквальных имен файлов это различие не имеет значения (системные вызовы выполняют сопоставление), но при использовании globs это имеет значение, и, к сожалению, вы должны сделать свой собственный переводмежду двумя формами.


Поддержка NUL (0x0) символов .:

Я не думаю, NUL символы.в именах файлов это реальная проблема:

  • Большинство POSIX-подобных оболочек (bash, dash, ksh) игнорировать NUL символов.в командной строке - zsh - единственное исключение.
  • Даже если это не было проблемой, согласно Wikipedia , большинство систем Unix делают не поддержка NUL символов.в именах файлов.

Кроме того, попытка передать литерал с NUL в функцию Perl system() прерывает вызов, предположительно, потому что строка передана sh -c обрезано на первом NUL:

system "echo 'a\x{0}b'";  # BREAKS
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...