Как Perl system () может распечатать команду, которую он выполняет?

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

Обычно это полезно, но иногда я хочу посмотреть, что происходит за кулисами. Как сделать так, чтобы выполненные команды выводились на терминал, а выходные данные этих программ выводились на терминал? Это будет .batэквивалентно "@echo on".

19.08.2008 23:47:00
6 ОТВЕТОВ
РЕШЕНИЕ

Как я понимаю, system () напечатает результат команды, но не назначит ее. Например.

[daniel@tux /]$ perl -e '$ls = system("ls"); print "Result: $ls\n"'
bin   dev  home  lost+found  misc  net  proc  sbin     srv  System  tools  var
boot  etc  lib   media       mnt   opt  root  selinux  sys  tmp     usr
Result: 0

Обратные метки будут захватывать выходные данные команды, а не печатать ее:

[daniel@tux /]$ perl -e '$ls = `ls`; print "Result: $ls\n"'
Result: bin
boot
dev
etc
home
lib

и т.д...

Обновление: если вы хотите напечатать имя команды, являющейся также system (), я думаю, что подход Радда хорош. Повторяется здесь для консолидации:

sub execute {
    my $cmd = shift;
    print "$cmd\n";
    system($cmd);
}

my $cmd = $ARGV[0];
execute($cmd);
10
23.05.2017 12:01:23

Вместо этого используйте open. Затем вы можете захватить вывод команды.

open(LS,"|ls");
print LS;
5
20.08.2008 06:05:47

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

sub execute {
    my $cmd = shift;
    print "$cmd\n";
    system($cmd);
}

my $cmd = $ARGV[0];
execute($cmd);

А затем увидеть это в действии:

pbook:~/foo rudd$ perl foo.pl ls
ls
file1   file2   foo.pl
19
20.08.2008 01:47:02

Хм, интересно, как разные люди отвечают на это по-разному. Мне кажется, что mk и Daniel Fone интерпретировали это как желание видеть / манипулировать stdout команды (ни одно из их решений не захватывает stderr fwiw). Я думаю, что Радд стал ближе. Единственный поворот, который вы могли бы предпринять в ответе Радда, - переписать встроенную команду system () своей версией, чтобы вам не пришлось переписывать существующий код для использования его команды execute ().

используя его подпрограмму execute () из поста Радда, вы можете получить что-то вроде этого в верхней части своего кода:

if ($DEBUG) {
   *{"CORE::GLOBAL::system"} = \&{"main::execute"};
}

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

  # importing into either the calling or global namespace _must_ be
  # done from import().  Doing it elsewhere will not have desired results.
  delete($opts{handle_system});
  if ($do_system) {
    if ($do_system eq 'local') {
      *{"$callpkg\::system"} = \&{"$_package\::system"};
    } else {
      *{"CORE::GLOBAL::system"} = \&{"$_package\::system"};
    }
  }
2
23.05.2017 12:08:36
Большая проблема с заменой глобальной systemкоманды состоит в том, что она systemимеет сложный прототип, который не может быть реплицирован системой, заданной пользователем. В результате некоторый код просто не будет работать, если вы замените его systemна свою версию.
cjm 27.09.2010 04:51:59

Другой метод, который можно комбинировать с другими, упомянутыми в ответах, - использовать teeкоманду. Например:

open(F, "ls | tee /dev/tty |");
while (<F>) {
    print length($_), "\n";
}
close(F);

Это будет как распечатывать файлы в текущем каталоге (как следствие tee /dev/tty), а также распечатывать длину каждого прочитанного имени файла.

1
20.08.2008 06:33:49

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

sub execute {
  my $cmd = shift;
  print "$cmd\n";
  my $ret = `$cmd`;
  print $ret;
  return $ret;
}
4
20.08.2008 16:33:40