Когда подходящее (и неправильное время) использовать обратные кавычки?

StackOverflow https://stackoverflow.com/questions/115809

  •  02-07-2019
  •  | 
  •  

Вопрос

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

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

  `cp $from $to`;
}

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

Это было полезно?

Решение

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

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

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

Я бы рекомендовал использовать IPC::Система::Простой модуль 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::Система::Простой является чистым Perl, работает на версии 5.6.0 и выше и не имеет каких-либо зависимостей, которые обычно не входят в состав вашего дистрибутива Perl.(В Windows это зависит от Win32::модуль, который поставляется как с ActiveState, так и с Strawberry Perl).

Отказ от ответственности:Я автор IPC::Система::Простой, поэтому я могу проявить некоторую предвзятость.

Другие советы

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

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

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

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

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

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

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

if ($?) {
   die "Error running [$cmd]";
}

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

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

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

В Perl всегда есть несколько способов сделать все, что вы захотите.Основная цель обратных кавычек — поместить стандартный вывод команды оболочки в переменную 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 WHO, "who|"
    or die "who failed";
while(<WHO>) {
    # Do something with each line
}
close WHO;

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

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

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

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

my $user = `/bin/whoami`;

или

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

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

Ваш пример плох, потому что для этого существуют встроенные функции Perl, которые переносимы и обычно более эффективны, чем альтернатива с обратной кавычкой.

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

Обратные кавычки следует использовать только тогда, когда вы хотите захватить выходные данные.Использование их здесь «выглядит глупо». Это подскажет, что кто -либо, кто смотрит на ваш код в том, что вы не очень знакомы с Perl.

Используйте обратные кавычки, если хотите сохранить вывод.Используйте систему, если хотите запустить команду.Одним из преимуществ, которое вы получите, является возможность проверить статус возврата.Используйте модули, где это возможно, для переносимости.В этом случае File::Copy отвечает всем требованиям.

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

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

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

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

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

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

У Perl раздвоение личности.С одной стороны, это отличный язык сценариев, способный заменить использование оболочки.При таком разовом использовании «я наблюдаю за результатом» обратные кавычки удобны.

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

Помимо вышесказанного, системную функцию следует использовать, когда вывод команды не используется.

Обратные кавычки — для любителей.Пуленепробиваемое решение — это «Безопасное открытие трубы» (см. «Man Perlipc»).Вы выполняете свою команду в другом процессе, что позволяет вам сначала возиться со STDERR, setuid и т. д.Преимущества:оно делает нет полагаться на оболочку для анализа @ARGV, в отличие от open("$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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top