我需要在 Perl 中使用 system() 运行 shell 命令。例如,
system('ls')
系统调用将打印到STDOUT,但我想将输出捕获到一个变量中,以便将来可以使用我的 Perl 代码进行处理。
这就是反引号的用途。来自perldoc perlfaq8
:
为什么我不能用 得到命令的输出
system()
?您混淆了
system()
反引号 (``) 的目的。system()
运行命令并返回退出状态信息(作为 16 位值:低 7 位是进程死亡的信号,如果有的话,高 8 位是实际退出值)。反引号 (``) 运行命令并将它发送到 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 的打印,因为它被重定向到一个变量。
此示例不适用于与用户交互的命令,除非您已准备好答案。为此,您可以使用一堆 shell 命令来使用类似这样的东西:
$variable=`cat answers.txt|some_command some args`;
在answers.txt文件中,您应该为 some_command 正常工作准备所有答案。
我知道这不是最好的编程方式 :) 但这是实现目标的最简单方式,特别是对于 Bash 程序员。
当然,如果输出更大(ls
带有子目录),您不应该一次获得所有输出。以与读取常规文件相同的方式读取命令:
open CMD,'-|','your_command some args' or die $@;
my $line;
while (defined($line=<CMD>)) {
print $line; # Or push @table,$line or do whatever what you want processing line by line
}
close CMD;
无需额外 Bash 调用即可处理长命令输出的附加扩展解决方案:
my @CommandCall=qw(find / -type d); # Some example single command
my $commandSTDOUT; # File handler
my $pid=open($commandSTDOUT),'-|'); # There will be an implicit fork!
if ($pid) {
#parent side
my $singleLine;
while(defined($singleline=<$commandSTDOUT>)) {
chomp $line; # Typically we don't need EOL
do_some_processing_with($line);
};
close $commandSTDOUT; # In this place $? will be set for capture
$exitcode=$? >> 8;
do_something_with_exit_code($exitcode);
} else {
# Child side, there you really calls a command
open STDERR, '>>&', 'STDOUT'; # Redirect stderr to stdout if needed. It works only for child - remember about fork
exec(@CommandCall); # At this point the child code is overloaded by an external command with parameters
die "Cannot call @CommandCall"; # Error procedure if the call will fail
}
如果您使用这样的程序,您将捕获所有程序输出,并且您可以逐行进行所有处理。祝你好运 :)
我想运行 system() 而不是反引号,因为我想查看rsync --progress
. 但是,我还想根据返回值捕获输出以防出现问题。(这是用于备份脚本)。这就是我现在使用的:
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;
unlink($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;
}
编辑:使用unlink
而不是system("rm ...")