当我尝试列出所有 ASCII 字符及其 Unicode 类别时,我发现打印控制字符
ESCAPE
(U+001b
) 会停止命令。
例如,只有输入
Ctrl-C
(27
是 0x1b
的十进制形式),以下命令才会返回:
PS C:\> "{0}" -F ([Char]27)
有什么办法可以防止在命令中打印
U+001b
时停止命令吗?
问题是PowerShell自动为自身启用VT(虚拟终端)支持,这意味着ESCAPE字符(U+001B
),即Unicode代码点
0x1b
(十进制)的字符27
)似乎总是被解释为 作为 VT (ANSI) 转义序列的开始: 因此,
alone是一个不完整转义序列,因此会导致表面上挂起,仅在Windows上(在Unix类平台上,根本就没有可见输出)。
,但后续命令输入被接受并可以照常提交,但有一个例外:按 Enter alone不起作用,但如果您提交一个 space,然后按 Enter,则会执行无操作命令,并且提示符将恢复正常。
请注意,这同样适用于 Windows 终端中的常规控制台窗口和会话。
注意:以下假设您想要打印 ESCAPE 字符,就像 VT 支持被disabled
一样,即←
当该符号被解释为
右侧可打印字符而不是
←
(向左箭头,U+2190
)
用向左箭头替换实际的 ESCAPE 字符,以实现 显示输出:
$c = [char] 0x1b
'{0}' -f ($c -replace [char] 0x1b, [char] 0x2190)
虽然这可以避免“挂起”,但它具有以下限制
暂时禁用 VT 支持: 这是更全面的解决方案,但有以下
限制每个会话一次的性能损失。
如果您在脚本中明确重新打开 VT 支持(如下所示),则会尝试“间接”打印到控制台,例如通过将脚本的调用括在(...)
Write-Host
、 Out-Host
或
Write-Output
,将再次出现“挂起”。另一种方法是让 PowerShell
自动重新启用它,
下次它在交互式会话中打印提示符时。
# Compile a helper type with P/Invoke declarations for enabling / disabling VT support.
# Note: This incurs a once-per-session peformance penalty.
$consoleHelper = Add-Type -PassThru -Namespace "NS$PID" -Name ConsoleHelper -MemberDefinition @'
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool GetConsoleMode(IntPtr handle, out int mode);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr GetStdHandle(int handle);
public static void EnableVtSupport(bool enable = true) {
IntPtr outHandle = GetStdHandle(-11); // -11 == STD_OUTPUT_HANDLE
int mode;
GetConsoleMode(outHandle, out mode);
SetConsoleMode(outHandle, enable ? (mode | 0x4) : (mode & ~0x4)); // 0x4 == ENABLE_VIRTUAL_TERMINAL_PROCESSING
}
'@
# Temporarily turn off VT support.
$consoleHelper::EnableVtSupport($false)
# This now does NOT cause the "hang" and prints "←" instead.
'{0}' -f [char] 27
# Re-enable it (see caveat above).
$consoleHelper::EnableVtSupport($true)
# Verify that VT support is back on: the word "green" should print in green.
# Note: In PS 7+ you could use `e inside "..." to produce ESCAPE chars:
# "It ain't easy being `e[32mgreen`e[m."
'It ain''t easy being {0}[32mgreen{0}[m.' -f [char] 27