我想捕获一个命令的所有输出("rules": {
"no-undef": [2, "[A-Z][a-z]*Model"],
和STDOUT
),该命令也需要来自终端窗口的用户交互,即它读取STDERR
然后打印到STDIN
。
以下是我想要捕获输出的脚本的最小版本:
user.pl :
STDOUT
我尝试使用Capture::Tiny
:
p.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!";
但它不起作用,因为直到用户在终端输入输入后才会显示提示。
修改:
如果我用python脚本替换#! /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;
}
,它似乎工作正常。例如:
user.py :
user.pl
答案 0 :(得分:3)
TL / DR 有一个解决方案,它有点难看,但它有效。有一些小问题。
发生了什么?问题实际上在user.pl
。您提供的示例user.pl
的工作方式如下:首先将字符串Enter URL:
打印到其stdout
,然后刷新其stdout
然后从其中读取一行stdin
。 stdout
的刷新由perl自动发生:当您尝试使用stdin
(又名<..>
)从readline
读取时,perl会刷新stdout
。正是这样才能使这样的程序正常运行。不幸的是,当stdout
是tty(伪终端)时,perl似乎只实现了这种行为。如果没有,则在从stdout
读取之前不会刷新stdin
。这就是当您在交互式终端会话中执行脚本时脚本工作的原因,并且当您尝试捕获其输出时它无法正常工作(因为在这种情况下,它的stdout
连接到管道)。
如何解决此问题?由于user.pl
stdout
不是tty,因此IPC::Run
行为不正常,我们必须使用tty。 AFAIK,IPC::Run是唯一可以使用tty而不是普通管道捕获子进程输出的perl模块。不幸的是,当使用tty时,stdout
不允许我们仅重定向stdin
,它会强制我们重定向stdin
。因此,我们必须代表子进程处理父进程中p.pl
的读取(yikes!)。以下是IPC::Run
使用#!/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
:
Enter URL:
所以这有点难看。例如,我们尝试检测子进程何时等待用户输入(通过查找字符串IPC::Run
),当它发生时,我们读取父进程中的用户输入,然后将其传递给子进程。另请注意,我们必须自己实现tee功能,因为readline
不提供此功能。
有一些警告。我们处理用户输入的方式,如果子进程使用类似<STDIN>
库的东西来支持行编辑,这将不起作用,因为我们使用简单的stdout
在父进程中完成所有读取操作。此外,由于在幕后使用tty而不是管道,因此所有用户输入都将回显到$in
。因此,无论用户在提示符中输入什么内容,我们都会将其放入$out
以将其发送到流程,并将其从流程中返回(通过$out
变量)。但由于我们的终端也有回音,文本会出现两次。一种解决方案是过滤__DIR__
以删除用户输入并阻止我们打印它。
最后,这不适用于Windows。
答案 1 :(得分:0)
将输入提示直接写入tty。
open TTY, '>', '/dev/tty'; # or 'con' in Windows
print TTY "Enter URL:";
my $ans = <STDIN>;
...