perl – 如何捕获命令的所有输出,这也需要终端的用户输入?
我想捕获一个命令的所有输出(STDOUT和STDERR),该命令也需要来自终端窗口的用户交互,即它读取STDIN然后将某些内容打印到STDOUT.
这是我想要捕获输出的脚本的最小版本: user.pl: #! /usr/bin/env perl use feature qw(say); use strict; use warnings; print "Enter URL: "; my $ans = <STDIN>; # do something based on $ans say "Verification code: AIwquj2VVkwlWEBwway"; say "Access Token: bskjZO8iZotv!"; 我尝试使用 p.pl: #! /usr/bin/env perl use feature qw(say); use strict; use warnings; use Capture::Tiny qw(tee_merged); my $output = tee_merged { #STDOUT->autoflush(1); # This does not work system "user.pl"; }; if ( $output =~ /Access Token: (.*)$/ ) { say $1; } 但它不起作用,因为直到用户在终端输入输入后才显示提示. 编辑: 如果我用python脚本替换user.pl,它似乎工作正常.例如: user.py: #! /usr/bin/env python3 ans = input( 'Enter URL: ' ) # do something based on $ans print( 'Verification code: AIwquj2VVkwlWEBwway' ) print( 'Access Token: bskjZO8iZotv!' ) 解决方法
TL / DR有一个解决方案,它有点难看,但它的工作原理.有一些小问题.
这是怎么回事?问题实际上在user.pl中.您提供的示例user.pl的工作方式如下:首先将字符串Enter URL:打印到其标准输出,然后刷新其标准输出,然后从标准输入读取一行. stdout的刷新是由perl自动发生的:当你尝试从stdin读取< ..> (又名readline),perl刷新标准输出.正是这样才能使这样的程序正常运行.不幸的是,当stdout是tty(伪终端)时,perl似乎只实现了这种行为.如果没有,它在从stdin读取之前不会刷新stdout.这就是当您在交互式终端会话中执行脚本时脚本工作的原因,并且当您尝试捕获其输出时它无法正常工作(因为在这种情况下,它的stdout连接到管道). 如何解决这个问题?由于user.pl在stdout不是tty时行为不正常,我们必须使用tty. AFAIK,IPC::Run是唯一可以使用tty而不是普通管道捕获子进程输出的perl模块.不幸的是,当使用tty时,IPC :: Run不允许我们仅重定向stdout,它迫使我们重定向stdin.因此,我们必须代表子进程处理父进程中的stdin读取(yikes!).这是使用IPC :: Run的p.pl的示例实现: #!/usr/bin/perl use strict; use warnings; use IO::Handle; use IPC::Run; my $complete_output=''; my $in=''; my $out=''; my $h=IPC::Run::start ['./user.pl'],'<pty<',$in,'>pty>',$out; while ($h->pumpable) { $h->pump; print $out; STDOUT->flush; if ($out eq 'Enter URL: ') { $in.=<STDIN>; } $complete_output.=$out; $out=''; } $h->finish; # do something with $complete_output here 所以这有点难看.例如,我们尝试检测子进程何时等待用户输入(通过查找字符串输入URL :),当它发生时,我们读取父进程中的用户输入,然后将其传递给子进程.另请注意,我们必须自己实现tee功能,因为IPC :: Run不提供. 有一些警告.我们处理用户输入的方式,如果子进程使用类似readline库的东西来支持行编辑,这将不起作用,因为我们使用简单的< STDIN>进行父进程中的所有读取.此外,因为在幕后使用tty而不是管道,所有用户输入都将回显到stdout.因此无论用户在提示符中输入什么内容,我们都将其放入$in中以将其发送到进程并从进程中返回(通过$out变量).但由于我们的终端也有回音,文本会出现两次.一种解决方案是过滤$out以删除用户输入并阻止我们打印它. 最后,这不适用于Windows. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |