在Activestate Perl中重定向stdin/stderr/stdout时写入控制台



我有以下代码要写入Windows命令控制台:

use Win32::Console;
my $console = new Win32::Console(Win32::Console::STD_ERROR_HANDLE());
my $defaultAttribute = $console->Attr();
my $defaultFG = ($defaultAttribute & 0x0F);
my $defaultBG = ($defaultAttribute & 0xF0);
$console->Attr($defaultBG | $Win32::Console::FG_LIGHTGREEN);
$console->Write("blah blah");
$console->Attr($defaultAttribute);

如果用户在调用我的脚本时重定向STDERR,则此代码失败:

perl myscript.pl 2> foo

如何在不引用某个标准句柄的情况下获得进程所连接的Win32控制台的句柄,这样用户进行的重定向就无关紧要了?

我想要的效果是能够在正常程序输出后立即在控制台上写一条消息,而不考虑任何重定向,这与bash内置的time命令类似。本质上类似于在Unix中打开和写入/dev/tty

我尝试过my $console = new Win32::Console()分配一个新控制台,然后是$console->Display(),但这完全是错误的。

在问了这个问题之后,我更深入地研究了一下,并能够通过使用一个讨厌的黑客来解决它:

use Win32API::File qw(createFile);
use Win32::Console;
my $handle = createFile('CONOUT$', 'rwke') or die "conout$: $^En";
# my $console = new Win32::Console($handle) or die "new console: $^En";
my $console = bless {handle => $handle}, 'Win32::Console';

我查看了Win32::Consolenew()函数的代码,发现它只是创建了一个包含控制台句柄的散列。如果参数指定stdin/stdout/stderr,它只检索关联的句柄,否则它将创建一个新的控制台屏幕缓冲区并使用该句柄。

因此,我只是手动创建了Win32::Console对象,该对象包含CreateFile返回的控制台句柄。

因此,现在perl myscript.pl > nul 2> nul < nul将在命令行正下方的屏幕上写入blah blah

如果有人提出一个更好的答案,我会接受的。

根据AllocConsole()文档(C++文档,但概念相同):

进程只能与一个控制台相关联,因此如果调用进程已经有控制台,则AllocConsole函数将失败。进程可以使用FreeConsole函数将自己从当前控制台分离,然后可以调用AllocConsole创建新控制台,或调用AttachConsole连接到另一个控制台。

由于你的控制台已经被重定向,看起来你对此无能为力;即使分离控制台并分配一个新控制台,新控制台也会继承重定向。在C++中,您可以使用SetStdHandle()API强制标准句柄指向不同的文件或设备,但我找不到任何与之相当的Perl。

最新更新