Как Perl system () может распечатать команду, которую он выполняет? - PullRequest
11 голосов
/ 20 августа 2008

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

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

Ответы [ 6 ]

19 голосов
/ 20 августа 2008

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

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
10 голосов
/ 20 августа 2008

Как я понимаю, 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

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

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

и т.д ...

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

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

my $cmd = $ARGV[0];
execute($cmd);
5 голосов
/ 20 августа 2008

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

open(LS,"|ls");
print LS;
4 голосов
/ 20 августа 2008

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

sub execute {
  my $cmd = shift;
  print "$cmd\n";
  my $ret = `$cmd`;
  print $ret;
  return $ret;
}
2 голосов
/ 20 августа 2008

Хм, интересно, как разные люди отвечают на это по-разному. Мне кажется, что 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"};
    }
  }
1 голос
/ 20 августа 2008

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

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

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

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