Мне нужно запустить команду оболочки с помощью system() в Perl. Например,
system('ls')
Системный вызов будет печатать на STDOUT, но я хочу записать вывод в переменную, чтобы я мог делать будущую обработку с помощью своего кода Perl.
Мне нужно запустить команду оболочки с помощью system() в Perl. Например,
system('ls')
Системный вызов будет печатать на STDOUT, но я хочу записать вывод в переменную, чтобы я мог делать будущую обработку с помощью своего кода Perl.
Для чего нужны обратные сигналы. Из perldoc perlfaq8
:
Почему я не могу получить вывод команды с
system()
?Вы путаете цель
system()
и обратных ссылок (` `).system()
запускает команду и возвращает информацию о статусе выхода (в виде 16-битного значения: низкие 7 бит - это сигнал, с которого процесс умер, если он есть, и высокие 8 бит - это фактическое значение выхода). Backticks (``) запускают команду и вернуть то, что он отправил в STDOUT.my $exit_status = system("mail-users"); my $output_string = `ls`;
Подробнее см. perldoc perlop
.
IPC::Run
- мой любимый модуль для такого рода задач. Очень мощный и гибкий, а также тривиально простой для небольших случаев.
use IPC::Run 'run';
run [ "command", "arguments", "here" ], ">", \my $stdout;
# Now $stdout contains output
Просто используйте пример bash:
$variable=`some_command some args`;
вот и все. вы не увидите на выходе каких-либо распечаток в STDOUT, потому что это перенаправлено на переменную. Этот пример неприменим для команды, которая взаимодействует с пользователем, за исключением того, что вы подготовили ответы. для этого вы можете использовать что-то вроде этого, используя стек команд оболочки:
$variable=`cat answers.txt|some_command some args`;
внутри файла ответов .txt вы должны подготовить все ответы для some_command, чтобы правильно работать.
Я знаю, что это не лучший способ программирования:), но это самый простой способ достижения цели, специально для программистов bash.
Конечно, если вывод больше (ls с подкаталогом), вы не должны получать весь вывод сразу. Прочитайте команду так же, как вы читаете файл regullar:
open CMD,'-|','your_command some args' or die [email protected];
my $line;
while (defined($line=<CMD>)) {
print $line; #or push @table,$line or do whatewer what you want processing line by line
}
close CMD;
Я хотел запустить system() вместо backtacks, потому что мне хотелось увидеть результат rsync --progress
. Тем не менее, я также хотел записать результат в случае, если что-то пойдет не так, в зависимости от возвращаемого значения. (Это для резервного копирования script). Это то, что я использую сейчас:
use File::Temp qw(tempfile);
use Term::ANSIColor qw(colored colorstrip);
sub mysystem {
my $cmd = shift; #"rsync -avz --progress -h $fullfile $copyfile";
my ($fh, $filename) = tempfile();
# http://stackoverflow.com/a/6872163/2923406
# I want to have rsync progress output on the terminal AND capture it in case of error.
# Need to use pipefail because 'tee' would be the last cmd otherwise and hence $? would be wrong.
my @cmd = ("bash", "-c", "set -o pipefail && $cmd 2>&1 | tee $filename");
my $ret = system(@cmd);
my $outerr = join('', <$fh>);
if ($ret != 0) {
logit(colored("ERROR: Could not execute command: $cmd", "red"));
logit(colored("ERROR: stdout+stderr = $outerr", "red"));
logit(colored("ERROR: \$? = $?, \$! = $!", "red"));
}
close $fh;
system("rm $filename");
return $ret;
}
# and logit() is sth like:
sub logit {
my $s = shift;
my ($logsec,$logmin,$loghour,$logmday,$logmon,$logyear,$logwday,$logyday,$logisdst)=localtime(time);
$logyear += 1900;
my $logtimestamp = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$logyear,$logmon+1,$logmday,$loghour,$logmin,$logsec);
my $msg = "$logtimestamp $s\n";
print $msg;
open LOG, ">>$LOGFILE";
print LOG colorstrip($msg);
close LOG;
}