Должен ли я избежать аргументов оболочки в Perl? - PullRequest
14 голосов
/ 06 марта 2009

При использовании вызовов system () в Perl, вам нужно экранировать аргументы оболочки или это делается автоматически?

Аргументы будут вводиться пользователем, поэтому я хочу убедиться, что это не может быть использовано.

Ответы [ 5 ]

37 голосов
/ 06 марта 2009

Если вы используете system $cmd, @args вместо system "$cmd @args" (массив, а не строка), вам не нужно экранировать аргументы, потому что не вызывается оболочка (см. system ). system {$cmd} $cmd, @args также не будет вызывать оболочку, даже если $ cmd содержит метасимволы, а @args пуст (это задокументировано как часть exec ). Если аргументы поступают из пользовательского ввода (или другого ненадежного источника), вы все равно захотите отменить их. См. -T в документах perlrun и документах perlsec .

Если вам нужно прочитать вывод или отправить ввод в команду, qx и readpipe не имеют эквивалента. Вместо этого используйте open my $output, "-|", $cmd, @args или open my $input, "|-", $cmd, @args, хотя это не переносимо, поскольку требует реального fork, что означает только Unix ... Я думаю. Может быть, он будет работать на Windows с его имитированной вилкой. Лучшим вариантом будет что-то вроде IPC :: Run , который также будет обрабатывать случай передачи команд другим командам, которые не будут обрабатываться ни формой multi-arg системы, ни формой open с 4 аргументами.

13 голосов
/ 08 марта 2009

В Windows ситуация несколько неприятнее. По сути, все программы Win32 получают одну длинную строку командной строки - оболочка (обычно cmd.exe) может сначала выполнить некоторую интерпретацию, удаляя, например, перенаправления < и >, но она не разбить его на границы слов для программы. Каждая программа должна делать это самостоятельно (если они хотят - некоторые программы не беспокоятся). В программах на C и C ++ подпрограммы, предоставляемые библиотеками времени выполнения, поставляемыми с цепочкой инструментов компилятора, обычно выполняют этот шаг синтаксического анализа перед вызовом main().

Проблема, как правило, , вы не знаете, как данная программа будет анализировать командную строку . Многие программы скомпилированы с некоторой версией MSVC ++, чьи причудливые правила синтаксического анализа описаны здесь , но многие другие скомпилированы с разными компиляторами, которые используют разные соглашения.

Это усугубляется тем фактом, что cmd.exe имеет свои собственные причудливые правила синтаксического анализа. Символ каретки (^) обрабатывается как escape-символ, который заключает в кавычки следующий символ, а текст внутри двойных кавычек обрабатывается как кавычка, если встречается список хитрых критериев (подробные сведения о кровопролитии см. cmd /?). Если ваша команда содержит какие-то странные символы, для cmd.exe очень легко понять, какие части текста «заключены в кавычки», а какие не должны синхронизироваться с вашей целевой программой, и все адские разрывы исчезнут.

Итак, самый безопасный подход к экранированию аргументов в Windows:

  1. Экранировать аргументы так, как этого требует логика синтаксического анализа командной строки вызываемой вами программы. (Надеюсь, вы знаете, что это за логика; если нет, попробуйте несколько примеров и догадайтесь.)
  2. Объедините экранированные аргументы с пробелами.
  3. Префикс каждый не алфавитно-цифровой символ результирующей строки с ^.
  4. Добавлять любые перенаправления или другие хитрости оболочки (например, объединяя команды с &&).
  5. Запустите команду с system() или обратными галочками.
2 голосов
/ 19 ноября 2011
 sub esc_chars {
  # will change, for example, a!!a to a\!\!a
     @_ =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"])/\\$1/g;
     return @_;
  }

http://www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-esc.html

1 голос
/ 03 ноября 2013

Если вы используете системную "$ cmd @args" (строку), вам придется экранировать аргументы, потому что вызывается оболочка.

К счастью, для строк в двойных кавычках необходимо экранировать только четыре символа:

"    - double quote
$    - dollar
@    - at symbol
\    - backslash
0 голосов
/ 13 декабря 2010

Ответы на ваш вопрос были очень полезны. В конце концов я последовал совету @ runrig, но затем использовал команду open module (3) основного модуля, чтобы я мог захватить вывод из STDERR, а также из STDOUT.

Пример кода open3 (), используемого с решением @ runrig, см. В моем связанном вопросе и ответе:
Вызов системных команд из Perl

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...