在Perl中运行system()命令时显示进度
我有一个执行一些任务的Perl脚本,其中一个是调用在Perl中运行system()命令时显示进度,perl,Perl,我有一个执行一些任务的Perl脚本,其中一个是调用系统命令“tar-cvf file.tar…” 这通常需要一些时间,因此我希望命令行回显进度指示器,例如在系统调用进行时回显到屏幕 我一直在四处挖掘,无意中发现了fork。这是最好的方式吗?是否可以分叉系统命令,然后创建while循环,检查分叉返回的$pid的状态 我还看到了对waitpid的引用。。。。我猜我也需要用这个 fork system("tar ... ") while ( forked process is still active
系统
命令“tar-cvf file.tar…”
这通常需要一些时间,因此我希望命令行回显进度指示器,例如
在系统
调用进行时回显到屏幕
我一直在四处挖掘,无意中发现了fork
。这是最好的方式吗?是否可以分叉系统
命令,然后创建while循环,检查分叉返回的$pid
的状态
我还看到了对waitpid
的引用。。。。我猜我也需要用这个
fork system("tar ... ")
while ( forked process is still active) {
print #
sleep 1
}
我找错树了吗
非常感谢
约翰我想试试这样的东西
open my $tar, "tar -cvf file.tar..... 2>&/dev/null |"
or die "can't fork: $!";
my $i = 0;
while (<$tar>) {
if( i++ % 1000 == 0 ) print;
}
close $tar or die "tar error: $! $?";
打开我的$tar,“tar-cvf file.tar…..2>&/dev/null”
或者死“不能叉:$!”;
我的$i=0;
而(){
如果(i++%1000==0)打印;
}
关闭$tar或死“tar错误:$!$?”;
Perl对此有一个很好的结构,称为“管道打开”。您可以在shell提示符下键入perldoc-f open
来了解更多信息
# Note the use of a list for passing the command. This avoids
# having to worry about shell quoting and related errors.
open(my $tar, '-|', 'tar', 'zxvf', 'test.tar.gz', '-C', 'wherever') or die ...;
下面是一个示例片段:
open(my $tar, '-|', 'tar', ...) or die "Could not run tar ... - $!";
while (<$tar>) {
print ".";
}
print "\n";
close($tar);
open(我的$tar'-|','tar',…)或die“无法运行tar…-$!”;
而(){
“打印”;
}
打印“\n”;
收盘价($焦油);
将“打印”替换为每隔10到100行左右打印一个哈希标记,以获得一个漂亮的标尺。一个不依赖于子进程写入任何类型输出的示例,只要它运行,大约每秒只打印一次点:
use POSIX qw(:sys_wait_h);
$|++;
defined(my $pid = fork) or die "Couldn't fork: $!";
if (!$pid) { # Child
exec('long_running_command', @args)
or die "Couldn't exec: $!";
} else { # Parent
while (! waitpid($pid, WNOHANG)) {
print ".";
sleep 1;
}
print "\n";
}
虽然可能需要更多的错误检查,而且CPAN上可能已经有了更好的东西。将这类工作抽象出来似乎很有希望,但我不确定它有多可靠。对于在长时间运行的任务中显示进度,您会发现它很有用——它可以实现您描述的“跨屏幕打印”功能。
$|++;
$|++;
open(my $tar, 'tar ... |') or die "Could not run tar ... - $!";
while ($file=<$tar>) {
print "$file";
}
print "\n";
close($tar);
打开(我的$tar,'tar…|')或死亡“无法运行tar…-$!”;
而($file=){
打印“$file”;
}
打印“\n”;
收盘价($焦油);
这将打印从tar接收的文件名。扩展Hobbs提供的内容,如果您希望将数据从子进程返回到父进程,则需要一个外部管道。我最终使用了tempfs,因为它像文件一样简单,但不会在磁盘上放置IO点击 **重要** 您需要退出子进程,因为否则“子”进程将沿着相同的脚本继续,您将得到双
print
语句。因此,在下面的示例中,foreach(@stdoutput)
将发生两次,尽管只在脚本中出现一次
$shm_id = time; #get unique name for file - example "1452463743"
$shm_file = "/dev/shm/$shm_id.tmp"; #set filename in tempfs
$| = 1; #suffering from buffering
print ("Activity Indicator: "); #No new line here
defined(my $pid = fork) or die "Couldn't fork: $!";
if (!$pid) { # Child
@stdoutput=`/usr/home/script.pl -o $parameter`; #get output of external command
open (SHM, ">$shm_file");
foreach (@stdoutput) {
print SHM ("$_"); #populate file in tempfs
}
close (SHM);
exit; #quit the child process (will not kill parent script)
} else { # Parent
while (! waitpid($pid, WNOHANG)) {
print ("\#"); # prints a progress bar
sleep 5;
}
}
print ("\n"); #finish up bar and go to new line
open (SHM, "$shm_file");
@stdoutput = <SHM>; #Now open the file and read it. Now array is in parent
close (SHM);
unlink ($shm_file); #deletes the tempfs file
chomp(@stdoutput);
foreach (@stdoutput) {
print ("$_\n"); #print results of external script
}
$shm\u id=时间#获取文件的唯一名称-示例“1452463743”
$shm_file=“/dev/shm/$shm_id.tmp”#以tempfs格式设置文件名
$| = 1; #饱受缓冲之苦
打印(“活动指示器:”)#这里没有新线路
已定义(my$pid=fork)或死“无法分叉:$!”;
如果(!$pid){Child
@stdoutput=`/usr/home/script.pl-o$parameter`#获取外部命令的输出
打开(SHM,“>$SHM_文件”);
foreach(@stdoutput){
打印SHM(“$”)#以tempfs填充文件
}
关闭(SHM);
退出;#退出子进程(不会终止父脚本)
}否则{#家长
而(!waitpid($pid,WNOHANG)){
打印(\ \ \ \);打印进度条
睡眠5;
}
}
打印(“\n”)#完成酒吧,进入新线
打开(SHM,“$SHM_文件”);
@标准输出=#现在打开文件并读取它。现在数组位于父级
关闭(SHM);
取消链接($shm_文件)#删除tempfs文件
chomp(@stdoutput);
foreach(@stdoutput){
打印(“$\un”);#打印外部脚本的结果
}
fork的返回值是子进程ID(如果是父进程),如果是子进程ID,则返回0,如果fork失败,则返回未定义。见perldoc-f fork。嗨,约翰……这正是我想要的!非常感谢您的回复和示例。当我看到示例代码时,我总是发现这要容易得多。关于john不需要逗号,我从示例中删除了它;-):-)-谢谢你,约翰。有一件事……这个方法是否只适用于向STDOUT写入内容的命令?使用tar zcvf时,它似乎工作得很好,但我使用的另一个命令不会将任何内容回显到STDOUT…..当我尝试打开此命令的管道时,我看不到打印内容“:(@john yes,只有在命令写入内容时才有效。如果子进程不写入任何内容,则需要使用不同的构造,如fork
/exec
/nonblockingwaitpid
/sleep
@Johan如果使用$+
,则您的代码示例会更好,以便立即打印点y、 :)您好,谢谢您在这方面的帮助。我也会尝试一下,看看哪种方法最适合。我喜欢上面的$|++提示。我一直在做一个选择(STDOUT)然后$|=1请对文件/进程句柄使用本地词汇表,而不是全局词汇表。我的perl有点生疏,我不知道这对文件句柄是可能的。谢谢,我下次会尝试。