我有以下代码要写入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::Console
中new()
函数的代码,发现它只是创建了一个包含控制台句柄的散列。如果参数指定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。