如何防止脚本运行时在控制台中显示输入

时间:2019-07-06 09:00:38

标签: powershell

我有一个脚本,该脚本运行多个代码循环,并在各个阶段都依赖于特定的输入来进行前进。该功能正在运行。我当前的问题是围绕用户在控制台窗口当前显示的位置上显示的用户提供的无关输入。

由于脚本的功能完好,因此我考虑忽略此问题,但是,我正在努力通过此脚本的控制台显示来追求高标准,并且我想知道一种禁用所有用户输入时间的方法,除非提示。我认为答案与能够命令输入缓冲区存储0个条目有关,或者可以根据需要禁用然后重新启用键盘。

我曾尝试在战略位置使用$ HOST.UI.RawUI.Flushinputbuffer()来防止字符显示,但我认为我可以将其放置在循环中的任何地方都不会完全阻止来自在代码执行期间显示(这对于确保在需要输入时不会传递任何东西非常有用)。我已经尝试查找解决方案,但是唯一可以操纵输入缓冲区的命令是上面的命令。我还尝试了$ host.UI.RawUI.KeyAvailable变量的战略实现,以检测执行期间的击键,然后尝试$ host.UI.RawUI.ReadKey()来确定这些击键是否是不需要的,如果不执行任何操作,则无论如何,击键仍然显示在控制台中。

我知道就读取逃脱循环的键而言,这段代码已被破坏了,但请耐心等待。我对这个示例进行了哈希处理,以便您可以看到需要帮助消除的问题。如果在执行此代码期间按住任意字母键,将看到不需要的输入。

$blinkPhase = 1

# Set Coordinates for cursor

$x = 106

$y = 16

$blinkTime = New-Object System.Diagnostics.Stopwatch

$blinkTime.Start()

$HOST.UI.RawUI.Flushinputbuffer()

do {

    # A fancy blinking ellipses I use to indicate when Enter should be pressed to advance.

    $HOST.UI.RawUI.Flushinputbuffer()

    while ($host.UI.RawUI.KeyAvailable -eq $false) {

        if ($blinkTime.Elapsed.Milliseconds -gt 400) {

            if ($blinkPhase -eq 1) {

                [console]::SetCursorPosition($x,$y)

                write-host ". . ." -ForegroundColor gray

                $blinkPhase = 2

                $blinkTime.Restart()

            } elseif ($blinkPhase -eq 2) {

                [console]::SetCursorPosition($x,$y)

                write-host "     "

                $blinkPhase = 1

                $blinkTime.Restart()

            }

        }

        start-sleep -m 10

    }

    # Reading for actual key to break the loop and advance the script.

    $key = $host.UI.RawUI.ReadKey()

} while ($key.key -ne "Enter")

预期结果是,当椭圆闪烁时,按住任意字符键都不会在控制台窗口中显示输入。实际的结果(没有错误消息)是在控制台窗口中显示了数量有限的不必要/不必要的输入IS,这使脚本看起来很凌乱,并且也干扰了闪烁的过程。

1 个答案:

答案 0 :(得分:1)

您要查找的是 不回声(打印)所按下的键,这可以通过以下方式完成:

$key = $host.UI.RawUI.ReadKey('IncludeKeyDown, NoEcho')

此外,您的测试何时按 Enter 的方法有缺陷 [1] ;请改用以下内容:

# ...
} while ($key.Character -ne "`r")

注意事项:自PSReadLine版本2.0.0-beta4起,一个错误导致$host.UI.RawUI.KeyAvailable报告误报,因此您的代码可能无法正常工作-请参见this GitHub issue

解决方法:请改用[console]::KeyAvailable,这无疑是更好的选择,因为您已使用光标定位命令明确地将目标对准了控制台(终端)环境。


顺便说一句:您可以通过使用线程作业 在后台线程中执行UI更新,而仅轮询以下内容,从而简化并提高解决方案的效率:前台的击键:

注意:需要ThreadJob模块,该模块是PowerShell Core 的标准配置,例如,在Windows上,PowerShell可以安装Install-Module ThreadJob -Scope CurrentUser

Write-Host 'Press Enter to stop waiting...'

# Start the background thread job that updates the UI every 400 msecs.
# NOTE: for simplicity, I'm using a simple "spinner" here.
$jb = Start-ThreadJob { 
  $i=0
  while ($true) { 
    [console]::Write("`r{0}" -f '/-\|'[($i++ % 4)])
    Start-Sleep -ms 400 
  }
}

# Start another thread job to do work in the background.
# ...

# In the foreground, poll for keystrokes in shorter intervals, so as 
# to be more responsive.
While (-not [console]::KeyAvailable -or ([console]::ReadKey($true)).KeyChar -ne "`r" ) {
  Start-Sleep -Milliseconds 50
}

Stop-Job $jb # Stop the background UI thread.

请注意在线程作业中使用[console]::Write(),因为实际上Write-Host的输出不会直接传递到控制台。


[1]您试图访问一个.Key属性,只有SystemConsoleKeyInfo返回的[[console]::ReadKey()]类型具有该属性; $host.UI.rawUI.ReadKey()返回类型[System.Management.Automation.Host.KeyInfo]中的近似等效项是.VirtualKeyCode,但是其特定类型有所不同,因此您不能(直接)将其与"Enter"进行比较;后一种类型的.Character返回实际按下的[char]实例,在 Enter 情况下为CR字符("`r")。