Когда подходящее время (и неправильное время) использовать обратные пометки - PullRequest
14 голосов
/ 22 сентября 2008

Многие начинающие программисты пишут такой код:

sub copy_file ($$) {
  my $from = shift;
  my $to = shift;

  `cp $from $to`;
}

Это плохо и почему? Должны ли когда-либо использоваться backticks? Если да, то как?

Ответы [ 14 ]

22 голосов
/ 23 сентября 2008

Несколько человек уже упоминали, что вы должны использовать обратные пометки, когда:

  • Вам нужно захватить (или подавить) вывод.
  • Не существует встроенной функции или модуля Perl для выполнения той же задачи, или у вас есть веская причина не использовать модуль или встроенный модуль.
  • Вы дезинфицируете свой ввод.
  • Вы проверяете возвращаемое значение.

К сожалению, такие вещи, как правильная проверка возвращаемого значения , могут быть довольно сложными. Он умер до сигнала? Дождался ли он до конца, но вернул ли смешной статус выхода? Стандартные способы интерпретации $? просто ужасны.

Я бы рекомендовал использовать IPC :: System :: Simple модуля capture() и system() функций, а не обратные пометки. Функция capture() работает так же, как обратные метки, за исключением того, что:

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

Команды также работают согласованно в операционных системах и версиях Perl, в отличие от встроенного в Perl system(), который может не проверять испорченные данные при вызове с несколькими аргументами в старых версиях Perl (например, 5.6.0 с несколькими аргументами) или который может вызвать оболочку в любом случае под Windows.

Например, следующий фрагмент кода сохранит результаты вызова perldoc в скаляр, исключит оболочку и выдает исключение, если страница не может быть найдена (поскольку perldoc возвращает 1).

#!/usr/bin/perl -w
use strict;
use IPC::System::Simple qw(capture);

# Make sure we're called with command-line arguments.
@ARGV or die "Usage: $0 arguments\n";

my $documentation = capture('perldoc', @ARGV);

IPC :: System :: Simple - это чистый Perl, работает на 5.6.0 и выше и не имеет никаких зависимостей, которые обычно не поставляются с вашим дистрибутивом Perl. (В Windows это зависит от модуля Win32 ::, который поставляется с ActiveState и Strawberry Perl).

Отказ от ответственности: я являюсь автором IPC :: System :: Simple , поэтому я могу показать некоторую предвзятость.

11 голосов
/ 22 сентября 2008

Правило простое: никогда не используйте обратные метки, если вы можете найти встроенные для выполнения той же работы или если они являются надежным модулем на CPAN, который сделает это за вас. Обратные ссылки часто полагаются на непереносимый код, и даже если вы не используете переменные, вы все равно можете открыть себе множество дыр в безопасности.

Никогда не используйте обратные метки с пользовательскими данными, если только вы не очень четко указали, что разрешено (а не то, что запрещено - вы пропустите)! Это очень, очень опасно.

6 голосов
/ 22 сентября 2008

Обратные метки следует использовать тогда и только тогда, когда вам нужно зафиксировать вывод команды. В противном случае должна использоваться system (). И, конечно, если есть функция Perl или CPAN-модуль, который выполняет эту работу, это следует использовать вместо любого из них.

В любом случае настоятельно рекомендуется две вещи:

Во-первых, очистить все входные данные: Используйте режим Taint (-T), если код подвергается возможному ненадежному вводу. Даже если это не так, убедитесь, что обрабатываете (или предотвращаете) такие забавные символы, как пробел или три вида кавычек.

Во-вторых, проверьте код возврата , чтобы убедиться, что команда выполнена успешно. Вот пример того, как это сделать:

my $cmd = "./do_something.sh foo bar";
my $output = `$cmd`;

if ($?) {
   die "Error running [$cmd]";
}
5 голосов
/ 22 сентября 2008

Другой способ перехватить стандартный вывод (в дополнение к коду pid и выходу) - использовать IPC :: Open3 , возможно, отрицательно сказываясь на использовании как системных, так и обратных тиков.

5 голосов
/ 22 сентября 2008

Используйте обратные метки, если вы хотите получить выходные данные команды.

В противном случае system() - лучший выбор, особенно если вам не нужно вызывать оболочку для обработки метасимволов или анализа команд. Вы можете избежать этого, передав список в system (), например system('cp', 'foo', 'bar') (однако вам лучше использовать модуль для этого конкретного примера :))

4 голосов
/ 22 сентября 2008

В Perl всегда есть несколько способов сделать что угодно. Основная задача обратных галочек заключается в получении стандартного вывода команды оболочки в переменную Perl. (В вашем примере все, что напечатает команда cp, будет возвращено вызывающей стороне.) Недостатком использования обратных галочек в вашем примере является то, что вы не проверяете возвращаемое значение команды оболочки; CP может потерпеть неудачу, и вы не заметите. Вы можете использовать это со специальной переменной Perl $ ?. Когда я хочу выполнить команду оболочки, я склонен использовать system :

system("cp $from $to") == 0
    or die "Unable to copy $from to $to!";

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

Вот надуманный пример, где могут быть полезны обратные пометки:

my $user = `whoami`;
chomp $user;
print "Hello, $user!\n";

Для более сложных случаев вы также можете использовать open в качестве канала:

open WHO, "who|"
    or die "who failed";
while(<WHO>) {
    # Do something with each line
}
close WHO;
3 голосов
/ 22 сентября 2008

Для случая, который вы показываете, лучше всего использовать модуль File :: Copy . Однако, чтобы ответить на ваш вопрос, всякий раз, когда мне нужно выполнить системную команду, я обычно полагаюсь на IPC :: Run3 . Он предоставляет множество функциональных возможностей, таких как сбор кода возврата и стандартный вывод ошибок.

3 голосов
/ 22 сентября 2008

Из справочной страницы perlop:

Это не значит, что вы должны выйти из ваш способ избежать спины, когда они правильный способ получить что-то сделанный. Perl был создан, чтобы быть клеем язык, и одна из вещей, это склеивает это команды. Просто понять, что вы получаете себя в.

2 голосов
/ 23 сентября 2008

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

my $user = `/bin/whoami`;

или

my $result = `/bin/cp $from $to`;

Говоря только "whoami" или "cp", вы рискуете случайно запустить команду, отличную от той, которую вы намеревались, если путь пользователя изменится - что является уязвимостью безопасности, которую злоумышленник может попытаться использовать.

1 голос
/ 22 сентября 2008

В общем, лучше использовать system вместо обратных кавычек, потому что:

  1. system предлагает вызывающей стороне проверить код возврата команды.

  2. система допускает нотацию «косвенного объекта», что более безопасно и добавляет гибкости.

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

  4. В обратных тегах используется минимальный синтаксис для тяжелой команды.

Одна из причин, по которой пользователи могут использовать обратные пометки вместо system , заключается в том, чтобы скрыть STDOUT от пользователя. Это проще и гибче достигается путем перенаправления потока STDOUT:

my $cmd = 'command > /dev/null';
system($cmd) == 0 or die "system $cmd failed: $?"

Кроме того, избавиться от STDERR легко:

my $cmd = 'command 2> error_file.txt > /dev/null';

В ситуациях, когда имеет смысл использовать обратные метки, я предпочитаю использовать qx {} , чтобы подчеркнуть, что происходит команда с большим весом.

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

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