PowerShell循环自动化交互命令行exe报重定向错误如何解决
老旧控制台部署工具批量自动化方案
问题梳理
- 现有一款运行稳定但操作繁琐的旧版命令行exe部署工具,启动后会依次弹出3项交互输入要求:
Enter The destination FolderEnter The ServerEnter the Password
- 需对10台左右服务器执行批量部署,除服务器地址外其余参数均已标准化,手动操作存在大量重复复制粘贴工作。
- 前期测试两种
SendKeys实现方案(直接调用、通过cmd.exe承载调用)均无实际执行效果,服务器文件未更新,但手动在PowerShell中逐次输入交互内容可正常运行。 - 后续通过
System.Diagnostics.Process重定向标准输入的方式可成功完成单台部署,但循环执行时首个进程结束后脚本直接崩溃,报错:Cannot Read Keys when Either application does not have a console or when a console inout has been redirected
SendKeys方案失效属于正常现象:这类老旧控制台程序通常直接读取底层控制台输入缓冲区,不响应Windows消息机制的模拟按键输入,和脚本写法无关。
问题根因
单进程执行成功但循环崩溃的核心原因:代码未等待进程完全执行结束就进入下一次循环,也没有在单次部署完成后释放进程、输入/输出流关联的资源,同时未重定向标准输出、标准错误流导致缓冲区占满,后续新建进程时控制台输入流状态异常触发报错。
可直接运行的实现脚本
以下脚本会循环遍历服务器列表,每次启动独立的进程实例完成部署,自动处理流读取、资源释放、超时控制逻辑,替换对应固定参数即可使用:
# 固定配置项,根据实际情况修改 $destFolder = "替换为实际目标文件夹路径" $password = "替换为实际部署密码" $exePath = "c:\MyProcess.exe" $exeArgs = "替换为进程实际启动参数" # 待部署的服务器列表 $serverList = @( "server-01", "server-02", "server-03", "server-04", "server-05", "server-06", "server-07", "server-08", "server-09", "server-10" ) foreach ($server in $serverList) { Write-Host "`n开始部署至服务器: $server" $process = $null $stdIn = $null $outEvent = $null $errEvent = $null try { # 每次循环新建独立进程实例,禁止复用 $process = New-Object System.Diagnostics.Process $process.StartInfo.FileName = $exePath $process.StartInfo.Arguments = $exeArgs $process.StartInfo.UseShellExecute = $false # 必须同时重定向三个标准流,避免缓冲区阻塞进程 $process.StartInfo.RedirectStandardInput = $true $process.StartInfo.RedirectStandardOutput = $true $process.StartInfo.RedirectStandardError = $true # 异步读取输出/错误流,避免缓冲区占满卡死 $outEvent = Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -Action { if ($EventArgs.Data) { Write-Host "[程序输出] $($EventArgs.Data)" } } $errEvent = Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -Action { if ($EventArgs.Data) { Write-Host "[程序错误] $($EventArgs.Data)" } } $process.Start() | Out-Null $process.BeginOutputReadLine() $process.BeginErrorReadLine() $stdIn = $process.StandardInput # 按交互顺序写入参数,短延迟适配老旧程序的输入读取节奏 $stdIn.WriteLine($destFolder) Start-Sleep -Milliseconds 200 $stdIn.WriteLine($server) Start-Sleep -Milliseconds 200 $stdIn.WriteLine($password) # 关闭标准输入,通知程序参数输入完毕 $stdIn.Close() $stdIn = $null # 等待进程执行,设置30秒超时避免无限挂起,可根据实际部署耗时调整 $waitResult = $process.WaitForExit(30000) if (-not $waitResult) { $process.Kill() Write-Warning "服务器 $server 部署超时,已强制终止进程" } else { Write-Host "服务器 $server 部署完成,进程退出码: $($process.ExitCode)" } } catch { Write-Error "部署到服务器 $server 时发生异常: $_" } finally { # 单次循环结束后强制清理所有关联资源,避免影响下一次循环 if ($outEvent) { Unregister-Event -SourceIdentifier $outEvent.Name -ErrorAction SilentlyContinue Remove-Job $outEvent -Force -ErrorAction SilentlyContinue } if ($errEvent) { Unregister-Event -SourceIdentifier $errEvent.Name -ErrorAction SilentlyContinue Remove-Job $errEvent -Force -ErrorAction SilentlyContinue } if ($stdIn) { $stdIn.Dispose() } if ($process) { $process.Dispose() } [GC]::Collect() [GC]::WaitForPendingFinalizers() } }
调优说明
- 如果运行时出现参数漏传、错位的情况,可适当调大参数写入后的
Start-Sleep延迟数值,建议每次调整增加100毫秒即可。 - 大文件部署场景可适当调大
WaitForExit的超时时间,单位为毫秒。 - 不要尝试复用同一个
Process对象执行多次部署,每次循环新建实例是避免流状态残留异常的核心要求。
内容的提问来源于stack exchange,提问作者Bnd10706
相关产品推荐
相关产品推荐

