Когда подходящее время (и неправильное время) для использования обратных сигналов?
Многие начинающие программисты пишут такой код:
sub copy_file ($$) {
my $from = shift;
my $to = shift;
`cp $from $to`;
}
Это плохо, и почему? Должны ли использоваться обратные сигналы? Если да, то как?
Ответы
Ответ 1
Несколько человек уже упомянули, что вы должны использовать обратные ссылки, когда:
- Вам нужно захватить (или подавить) вывод.
- Нет встроенной функции или модуля Perl для выполнения одной и той же задачи или у вас есть веская причина не использовать модуль или встроенный модуль.
- Вы дезинфицируете свой вход.
- Вы проверяете возвращаемое значение.
К сожалению, такие вещи, как проверка возвращаемого значения, могут быть довольно сложными. Он умер до сигнала? Завершилось ли это, но вернули смешной статус выхода? Стандартные способы интерпретации $?
просто ужасны.
Я бы рекомендовал использовать IPC::System::Simple модуль capture()
и system()
, а не backticks. Функция capture()
работает так же, как backticks, за исключением того, что:
- Он предоставляет подробную диагностику, если команда не запускается, убивается сигналом или возвращает неожиданное значение выхода.
- Он предоставляет подробную диагностику, если передал испорченные данные.
- Он обеспечивает простой механизм для указания допустимых значений выхода.
- Это позволяет вам вызывать backticks без оболочки, если вы хотите.
- Он обеспечивает надежные механизмы для предотвращения оболочки, даже если вы используете один аргумент.
Команды также работают последовательно между операционными системами и версиями Perl, в отличие от встроенного system()
Perl, который не может проверять испорченные данные при вызове с несколькими аргументами в более старых версиях 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, поэтому я могу показать некоторый уклон.
Ответ 2
Правило прост: никогда не используйте обратные ссылки, если вы можете найти встроенное приложение для выполнения той же работы или если это надежный модуль в CPAN, который сделает это за вас. Backticks часто полагаются на unportable код, и даже если вы не используете переменные, вы все равно можете открыть себя для множества дыр в безопасности.
Никогда не используйте обратные ссылки с пользовательскими данными, если вы не очень точно указали, что разрешено (не то, что запрещено, вы пропустите вещи)! Это очень, очень опасно.
Ответ 3
Backticks следует использовать тогда и только тогда, когда вам нужно записать вывод команды. В противном случае следует использовать system(). И, конечно же, если есть функция Perl или модуль CPAN, выполняющий задание, это должно использоваться вместо того, чтобы либо.
В любом случае настоятельно рекомендуется две вещи:
Во-первых, санировать все входы: Использовать режим Taint (-T), если код подвергается возможному ненадежному вводу. Даже если это не так, убедитесь, что вы обрабатываете (или предотвращаете) напуганные символы, такие как пробел или три вида цитат.
Во-вторых, проверьте код возврата, чтобы убедиться, что команда выполнена успешно. Вот пример того, как это сделать:
my $cmd = "./do_something.sh foo bar";
my $output = `$cmd`;
if ($?) {
die "Error running [$cmd]";
}
Ответ 4
Используйте обратные выходы, когда вы хотите собрать результат из команды.
В противном случае system()
- лучший выбор, особенно если вам не нужно вызывать оболочку для обработки метасимволов или синтаксического анализа команды. Вы можете избежать этого, передав список в system(), например system('cp', 'foo', 'bar')
(однако, вероятно, вам лучше использовать модуль для этого конкретного примера:))
Ответ 5
Другой способ захвата stdout (помимо кода pid и exit) заключается в использовании IPC:: Open3, который может отрицательно повлиять на использование обоих системы и обратных ссылок.
Ответ 6
В Perl всегда есть более чем один способ сделать что угодно. Первичной точкой backticks является получение стандартного вывода команды оболочки в Perl-переменную. (В вашем примере все, что печатает команда cp, будет возвращено вызывающей стороне.) Недостатком использования обратных шагов в вашем примере является то, что вы не проверяете возвращаемое значение команды оболочки; cp может потерпеть неудачу, и вы не заметите. Вы можете использовать это со специальной переменной Perl $?. Когда я хочу выполнить команду оболочки, я предпочитаю использовать систему:
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;
Ответ 7
Из справочной страницы "perlop":
Это не значит, что вы должны выйти из ваш способ избежать обратных шагов, когда они - правильный способ получить что-то сделанный. Perl был сделан клеем языка и одной из Клеи вместе - это команды. Просто понять, что вы получаете себя в.
Ответ 8
Для случая, который вы показываете с помощью File:: Copy, вероятно, лучше всего. Однако, чтобы ответить на ваш вопрос, всякий раз, когда мне нужно запустить системную команду, я обычно полагаюсь на IPC:: Run3. Он предоставляет множество функций, таких как сбор кода возврата, выход стандарта и ошибок.
Ответ 9
Независимо от того, что вы делаете, а также для дезинфекции ввода и проверки возвращаемого значения вашего кода, убедитесь, что вы вызываете любые внешние программы с их явным полным путем. например говорят
my $user = `/bin/whoami`;
или
my $result = `/bin/cp $from $to`;
Высказывание "whoami" или "cp" запускает риск случайного запуска команды, отличной от того, что вы планировали, если изменяется путь пользователя - это уязвимость системы безопасности, которую злоумышленник может попытаться использовать.
Ответ 10
В вашем примере плохо, потому что есть встроенные встроенные perl, чтобы сделать портативные и обычно более эффективными, чем альтернатива backtick.
Они должны использоваться только тогда, когда нет встроенной (или модульной) версии Perl. Это как для обратных вызовов, так и для вызовов system(). Backticks предназначены для захвата вывода выполненной команды.
Ответ 11
Backticks предполагается использовать только тогда, когда вы хотите захватить вывод. Использование их здесь "выглядит глупо". Это будет подсказывать любому, кто смотрит на ваш код, на то, что вы не очень хорошо знакомы с Perl.
Используйте обратные выходы, если вы хотите захватить вывод.
Используйте систему, если вы хотите запустить команду. Одно из преимуществ, которое вы получите, - это возможность проверить статус возврата.
Используйте модули, где это возможно, для переносимости. В этом случае File:: Copy соответствует счету.
Ответ 12
В общем, лучше использовать систему вместо обратных ссылок, потому что:
-
система побуждает вызывающего пользователя проверить код возврата команды.
-
система допускает нотацию "косвенного объекта", которая более безопасна и добавляет гибкости.
-
Backticks культурно привязаны к сценариям оболочки, что может не распространяться среди читателей кода.
-
Backticks используют минимальный синтаксис для того, что может быть тяжелой командой.
Одной из причин, по которой пользователи могут использовать backticks вместо 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';
В ситуациях, когда имеет смысл использовать backticks, я предпочитаю использовать qx {}, чтобы подчеркнуть, что есть команда с большим весом.
С другой стороны, иметь другой способ сделать это может действительно помочь. Иногда вам просто нужно посмотреть, что команда печатает в STDOUT. Backticks, когда они используются как в сценариях оболочки, являются правильным инструментом для задания.
Ответ 13
Perl имеет раздвоенную индивидуальность. С одной стороны, это отличный скриптовый язык, который может заменить использование оболочки. В этом виде одноразового использования I-watch-the-result удобнее использовать backticks.
При использовании языка программирования следует избегать обратных ссылок. Это недостаток ошибки
проверка и, если можно выполнить отдельные обратные шаги программы, эффективность
получил.
Помимо вышесказанного, системная функция должна использоваться, когда выходной сигнал команды не используется.
Ответ 14
Backticks для любителей. Пуленепробиваемое решение - "Безопасная труба открыта" (см. "Man perlipc" ). Вы выполняете команду в другом процессе, который позволяет вам сначала futz с помощью STDERR, setuid и т.д. Преимущества: он не полагается на оболочку для разбора @ARGV, в отличие от открытого ( "$ cmd $args |" ), что ненадежно, Вы можете перенаправить STDERR и изменить права пользователей без изменения поведения вашей основной программы. Это более подробно, чем обратные, но вы можете обернуть его в свою собственную функцию, например run_cmd ($ cmd, @args);
sub run_cmd {
my $cmd = shift @_;
my @args = @_;
my $fh; # file handle
my $pid = open($fh, '-|');
defined($pid) or die "Could not fork";
if ($pid == 0) {
open STDERR, '>/dev/null';
# setuid() if necessary
exec ($cmd, @args) or exit 1;
}
wait; # may want to time out here?
if ($? >> 8) { die "Error running $cmd: [$?]"; }
while (<$fh>) {
# Have fun with the output of $cmd
}
close $fh;
}