代替在脚本中使用pause
,使脚本在设置时间后自动退出对我来说很有用。这对我来说特别有用,因为这些脚本可以交互运行,也可以作为Start-Process -WindowStyle Hidden
的后台任务,其中pause
意味着进程可以永远挂起,但有了计时器,即使它在后台,它也会超时。我通过这个问题得到了这个解决方案:PowerShell,打破一个定时器与自定义按键
$t = 8; Write-Host "Exiting in $t seconds (press any key to exit now)" -NoNewLine
for ($i=0; $i -le $t; $i++) {
Start-Sleep 1; Write-Host -NoNewLine "."
if ([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true).Key
if ($key) { break }
}
}
但是,如果我在后台运行Start-Process -WindowStyle Hidden
, PowerShell会因为[Console]::KeyAvailable
而产生错误,因为在后台运行时没有可用的控制台。
Cannot see if a key has been pressed when either application does not have a
console or when console input has been redirected from a file. Try
Console.In.Peek.
At C:UsersBossInstall Chrome.ps1:36 char:78
+ ... ep 1; Write-Host -NoNewLine "."; if ([Console]::KeyAvailable) { $key ...
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationExcept
ion
+ FullyQualifiedErrorId : System.InvalidOperationException
•是否有一些方法可以调整代码,使其在后台运行时不会产生错误,同时仍然保留"按任何键退出";选项在交互式运行时?
•Try Console.In.Peek
是什么意思?
-
这是不是
Start-Process
的问题:-
当控制台应用程序在Windows上启动时,例如
powershell.exe
,控制台总是分配-即使它是隐藏(-WindowStyle Hidden
) -
除非您使用
-RedirectStandardInput
,否则该控制台的stdin流([Console]::In
)是不重定向的,正如[Console]::IsInputRedirected
返回$false
所反映的那样。 -
因此,您的代码将工作:
[Console]::KeyAvailable
将始终返回$false
,并且完整的超时时间将过去。
-
-
然而,你会看到
Start-Job
的问题:-
创建的后台作业使用隐藏的PowerShell子进程在后台运行代码,调用PowerShell实例的方式是通过stdin与它通信。
-
因此,
[Console]::KeyAvailable
会导致您看到的语句终止错误,如果错误消息中提到的原因之一适用:stdin被重定向(但确实存在控制台)。
-
工作区:
只要在[Console]::IsInputRedirected
返回$false
的条件下使用[Console]::KeyAvailable
:
$t = 8; Write-Host "Exiting in $t seconds (press any key to exit now)" -NoNewLine
for ($i=0; $i -le $t; $i++) {
Start-Sleep 1; Write-Host -NoNewLine "."
# Note the use of -not [Console]::IsInputRedirected
if (-not [Console]::IsInputRedirected -and [Console]::KeyAvailable) {
$key = [Console]::ReadKey($true).Key
if ($key) { break }
}
}
或者使用线程作业相反,使用Start-ThreadJob
,其中随PowerShell (Core) 7+而在中,Windows PowerShell可以按需安装,例如Install-Module ThreadJob -Scope CurrentUser
:
Start-ThreadJob { & 'C:UsersBossInstall Chrome.ps1' }
线程作业,如果可用的话,通常比常规的、基于子进程的后台作业更可取,因为线程作业更快、更轻量。
由于stdin的使用仅适用于进程间通信,因此[Console]::KeyAvailable
不会在线程作业中引起错误,并且-值得赞扬的是-仅在当前线程是前台线程时才注意按键。
使用Runspace
代替Start-Process
。运行空间可以与你的PSHost
相关联,因此你会看到两个好处,输出到你的控制台和按任何键取消脚本的能力。
try {
$ttl = 20
$iss = [initialsessionstate]::CreateDefault2()
$rs = [runspacefactory]::CreateRunspace($Host, $iss)
$rs.Open()
$ps = [powershell]::Create().AddScript({
param($ttl)
Write-Host "Exiting in $ttl seconds (press any key to exit now)" -NoNewLine
for ($i=0; $i -le $ttl; $i++) {
Start-Sleep 1; Write-Host -NoNewLine "."
if ([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true).Key
if ($key) { break }
}
}
Write-Host "Finished!" -ForegroundColor Green
}).AddParameter('ttl', $ttl)
$ps.Runspace = $rs
$async = $ps.BeginInvoke()
do {
$id = [System.Threading.WaitHandle]::WaitAny($async.AsyncWaitHandle, 200)
}
while($id -eq [System.Threading.WaitHandle]::WaitTimeout)
$ps.Stop()
$ps.EndInvoke($async)
}
finally {
$ps, $rs | ForEach-Object Dispose
}