如何捕获命令的所有输出,这也需要终端的用户输入?

时间:2016-09-27 20:00:19

标签: perl

我想捕获一个命令的所有输出("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

2 个答案:

答案 0 :(得分:3)

TL / DR 有一个解决方案,它有点难看,但它有效。有一些小问题。

发生了什么?问题实际上在user.pl。您提供的示例user.pl的工作方式如下:首先将字符串Enter URL:打印到其stdout,然后刷新其stdout然后从其中读取一行stdinstdout的刷新由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>;
...