沒有輸出時 Process.StandardOutput.Readline() 掛起
**注意:**我正在嘗試
packer.exe作為後台程序執行以解決azure-arm建構器的特定問題,我需要觀察輸出。我沒有使用Start-Process,因為我不想使用中間文件來消耗輸出。我將以下程式碼設置
packer.exe為在後台執行,這樣我就可以使用它的輸出並對某個日誌消息採取行動。這是一個更大的腳本的一部分,但這是有問題的一點,它的行為不正確:$builderDir = ( Split-Path -Parent $PSCommandPath ) Push-Location $builderDir # Set up the packer command to run asynchronously $pStartProps = @{ FileName = ( Get-Command -CommandType Application packer ).Source Arguments = "build -var-file ""${builderDir}\win-dev.pkrvars.hcl"" -only ""azure-arm.base"" ." UseShellExecute = $false RedirectStandardOutput = $true RedirectStandardError = $false LoadUserProfile = $true } $pStartInfo = New-Object ProcessStartInfo -Property $pStartProps $p = [Process]::Start($pStartInfo) while ( $null -ne ( $output = $p.StandardOutput.Readline() ) -or !$p.HasExited ) { # Do stuff }基本上,條件的
( $output = $p.StandardOutput.Readline() )一部分while似乎一直掛起,直到有更多的輸出要讀取。我不確定為什麼會這樣,因為StreamReader.Readline()應該返回要讀取的下一行,或者null是否沒有更多輸出。關於我期望獲得的日誌消息,我對此進行了相當多的處理,因此當沒有進一步的輸出消耗時讀取 STDOUT 時阻塞會使腳本無用。packer.exe在繼續執行的同時,它還在前台執行其他操作。我能夠在
Readline()確實讀取空行(的值"")的調試器中確認,這似乎發生在沒有進一步的輸出消耗時。這可能是相切的,但這也會導致調試器出錯。發生此問題時,VSCode 調試器會在此位置
$output = $p.StandardOutput.Readline()突出顯示幾秒鐘,然後調試器停止(一切都消失,不再跟踪變數等),直到Readline()停止阻塞並繼續執行,此時調試器似乎重新初始化跟踪的變數,監視的表達式等。所以當這種情況發生時我根本無法使用調試器。甚至PowerShell Integrated Console(與調試器一起使用的那個)也掛起,我無法輸入任何內容。對於完整的上下文,這個腳本的目標是讓
packer.exe我在不斷循環到:
- 顯示更多輸出
packer.exe- 檢查是否存在某個日誌消息
- 給
packer.exe一點時間嘗試自己做它需要做的事情- 如果它等待的時間太長,我會針對節點執行一個腳本,因為
packer.exe應該自己完成的操作可能會失敗
- 我正在這樣做,對於我正在解決的問題
Invoke-AzVMRunCommand,這在該州無法完成。packer.exe它必須在packer.exe執行本身的帶外執行。
- 應用解決方法後繼續建構,我只是繼續將輸出轉發
packer.exe到控制台,直到程序退出但是由於腳本在沒有輸出時掛起,所以第 4 步將永遠無法工作,因為我必須給加殼程序時間來嘗試自己完成配置,這也是我首先將它一起破解的全部原因。
怎麼
Readline()堵在這裡?我做錯了什麼嗎?無論我是在 Windows PowerShell 還是 PowerShell Core 中執行我的腳本,都會出現此行為。
StreamReader.ReadLine()被設計阻止。- 有一個非同步替代方法,
.ReadLineAsync()它返回一個Task<string>實例,您可以通過其屬性輪詢完成,.IsCompleted而不會阻塞您的前台執行緒(輪詢是 PowerShell 中的唯一選項,因為它沒有類似於 C# 的語言功能await)。這是一個簡化的範例,側重於從
StreamReader恰好是文件的實例中非同步讀取,新行僅定期添加到該文件中;用於Ctrl-C中止。如果您將其調整為您的標準輸出讀取程式碼,我希望程式碼能夠正常工作
System.Diagnostics.Process。# Create a sample input file. $n=3 1..$n > tmp.txt # Open the file for reading, and convert it to a System.IO.StreamReader instance. [IO.StreamReader] $reader = [IO.File]::Open("$pwd/tmp.txt", 'Open', 'Read', 'ReadWrite') try { $task = $reader.ReadLineAsync() # Start waiting for the first line. while ($true) { # Loop indefinitely to wait for new lines. if ($task.IsCompleted) { # A new line has been received. $task.Result # Output # Start waiting for the next line. $task.Dispose(); $task = $reader.ReadLineAsync(); } else { # No new line available yet, do other things. Write-Host '.' -NoNewline Start-Sleep 1 } # Append a new line to the sample file every once in a while. if (++$n % 10 -eq 0) { $n >> tmp.txt } } } finally { $reader.Dispose() }