Start-Process ile standart çıktı ve hata yakalama


112

Ve özelliklerine Start-Processerişirken PowerShell'in komutunda bir hata var mı ?StandardErrorStandardOutput

Aşağıdakileri çalıştırırsam çıktı alamam:

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.StandardOutput
$process.StandardError

Ancak çıktıyı bir dosyaya yönlendirirsem beklenen sonucu alırım:

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt

5
Bu özel durumda gerçekten Start-process'e ihtiyacınız var mı? ... $process= ping localhost # çıktıyı proses değişkenine kaydeder.
mjsr

1
Doğru. Geri dönüş ve tartışmaları halletmek için daha temiz bir yol arıyordum. Senaryoyu senin gösterdiğin gibi yazdım.
jzbruno

Yanıtlar:


128

Bu nasıl Start-Processnedense tasarlanmıştır. Dosyayı göndermeden almanın bir yolu:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode

7
Cevabınızı kabul ediyorum. Keşke kullanılmayan mülkler yaratmamış olsalar, bu çok kafa karıştırıcı.
jzbruno

6
Bir işlemi bu şekilde çalıştırmada sorun yaşıyorsanız , WaitForExit ve StandardOutput'ta ufak bir değişikliğe sahip olan stackoverflow.com/questions/11531068/… buradaki kabul edilen cevaba
bakın.ReadToEnd

3
-Verb run'u kullandığınızda, -NoNewWindow'a veya Yeniden Yönlendirme Seçenekleri'ne izin vermez
Maverick

15
Bu kod, StdErr ve StdOut'un sonuna kadar eşzamanlı olarak okunması nedeniyle bazı koşullar altında kilitlenecektir. msdn.microsoft.com/en-us/library/…
codepoke

8
@codepoke - bundan biraz daha kötü - önce WaitForExit çağrısını yaptığı için, yalnızca birini yeniden yönlendirse bile, akış arabelleği dolduğunda kilitlenebilir (çünkü işleme kadar ondan okumaya çalışmaz) çıkış yaptı)
James Manning

20

Soruda verilen kodda başlatma değişkeninin ExitCode özelliğini okumanın çalışması gerektiğini düşünüyorum.

$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode

(Örneğinizde olduğu gibi) -PassThruve -Waitparametrelerini eklemeniz gerektiğini unutmayın (bu beni bir süre yakaladı).


Ya argumentlist bir değişken içeriyorsa? Genişlemiyor gibi görünüyor.
OO

1
argüman listesini tırnak içine alırsınız. Işe yarar mı ? ... $ işlem = Başlat-İşlem -DosyaYolu ping -ArgumentList "-t localhost -n 1" -NoNewWindow -PassThru -Wait
JJones

çıktı powershell penceresinde nasıl gösterilir ve bir günlük dosyasına nasıl kaydedilir? Mümkün mü?
Murali Dhar Darshan

Kullanılamaz -NoNewWindowile-Verb runAs
Dragas

11

Ayrıca bu sorunu yaşadım ve birden fazla komutun çalıştırılması gerektiğinde işleri temizlemek için bir işlev oluşturmak için Andy'nin kodunu kullandım.

Nesne olarak stderr, stdout ve çıkış kodlarını döndürür. Unutulmaması gereken bir şey: işlev .\yolda kabul etmeyecektir ; tam yollar kullanılmalıdır.

Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $commandPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $commandArguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    [pscustomobject]@{
        commandTitle = $commandTitle
        stdout = $p.StandardOutput.ReadToEnd()
        stderr = $p.StandardError.ReadToEnd()
        ExitCode = $p.ExitCode
    }
}

İşte nasıl kullanılacağı:

$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"

İyi fikir, ama sözdizimi benim için çalışmıyor gibi görünüyor. Parametre listesi param ([type] $ ArgumentName) sözdizimini kullanmamalı mı? bu işleve örnek bir çağrı ekleyebilir misiniz?
Çilingir

"Unutulmaması gereken bir şey: işlev kabul etmeyecektir. \ Yolda; tam yollar kullanılmalıdır.": Şunları kullanabilirsiniz:> $ pinfo.FileName = Çözüm Yolu $ commandPath
Lupuz

9

ÖNEMLİ:

Yukarıda LPG tarafından sağlanan işlevi kullanıyoruz .

Ancak bu, çok fazla çıktı üreten bir işlemi başlattığınızda karşılaşabileceğiniz bir hatayı içerir. Bu nedenle, bu işlevi kullanırken bir kilitlenmeyle karşılaşabilirsiniz. Bunun yerine aşağıdaki uyarlanmış sürümü kullanın:

Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
  Try {
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $commandPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $commandArguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    [pscustomobject]@{
        commandTitle = $commandTitle
        stdout = $p.StandardOutput.ReadToEnd()
        stderr = $p.StandardError.ReadToEnd()
        ExitCode = $p.ExitCode
    }
    $p.WaitForExit()
  }
  Catch {
     exit
  }
}

Bu sorunla ilgili daha fazla bilgi MSDN'de bulunabilir :

Üst süreç p.StandardError.ReadToEnd'den önce p.WaitForExit'i çağırırsa ve alt süreç yeniden yönlendirilen akışı doldurmak için yeterli metin yazarsa, bir kilitlenme durumu oluşabilir. Üst süreç, alt sürecin çıkması için süresiz olarak bekler. Alt süreç, ebeveynin tam StandardError akışından okuması için süresiz olarak bekler.


3
Bu kod, MSDN bağlantınızın da açıkladığı zaman uyumlu ReadToEnd () çağrısı nedeniyle hala kilitleniyor.
bergmeister

1
Bu şimdi sorunumu çözmüş görünüyor. Neden asıldığını tam olarak anlamadığımı itiraf etmeliyim, ancak boş stderr işlemin bitirilmesini engelledi. Garip bir şey, uzun bir süre çalıştığı için, ancak Noel'den hemen önce aniden başarısız olmaya başladı ve birçok Java işleminin takılmasına neden oldu.
rhellem

8

Andy Arismendi'den ve LPG'den gelen örneklerle gerçekten sıkıntı yaşadım . Her zaman kullanmalısınız:

$stdout = $p.StandardOutput.ReadToEnd()

aramadan önce

$p.WaitForExit()

Tam bir örnek:

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode

"$ P.WaitForExit () 'den önce her zaman $ p.StandardOutput.ReadToEnd () kullanmalısınız" ifadesini nerede okudunuz? Tamponda, daha sonra daha fazla çıktıyı takiben tükenen çıktı varsa, yürütme satırı WaitForExit üzerindeyse ve işlem tamamlanmadıysa (ve daha sonra daha fazla stderr veya stdout çıktılarsa) bu eksik olacaktır ....
CJBS

Yukarıdaki yorumumla ilgili olarak, daha sonra büyük çıktı durumlarında kilitlenme ve arabellek taşması ile ilgili kabul edilen cevaba ilişkin yorumları gördüm, ancak bunun yanı sıra, sadece arabelleğin sonuna kadar okunduğu için bunun işlem anlamına gelmediğini bekliyorum. tamamlandı ve dolayısıyla kaçırılan daha fazla çıktı olabilir. Bir şey mi kaçırıyorum?
CJBS

@CJBS: "arabellek sonuna kadar okunduğu için işlemin tamamlandığı anlamına gelmez " - bu demek oluyor. Aslında, bu yüzden çıkmaza girebilir. "Sonuna kadar" okumak, " şimdi orada ne varsa oku" anlamına gelmez . Bu, okumaya başla ve akış kapanana kadar durma anlamına gelir, bu işlemin sonlandırılmasıyla aynıdır.
Peter Duniho

0

İşte standart System.Diagnostics.Process ile 3 yeni özelliği döndüren işlev sürümüm

Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
    Try {
        $pinfo = New-Object System.Diagnostics.ProcessStartInfo
        $pinfo.FileName = $commandPath
        $pinfo.RedirectStandardError = $true
        $pinfo.RedirectStandardOutput = $true
        $pinfo.UseShellExecute = $false
        $pinfo.WindowStyle = 'Hidden'
        $pinfo.CreateNoWindow = $True
        $pinfo.Arguments = $commandArguments
        $p = New-Object System.Diagnostics.Process
        $p.StartInfo = $pinfo
        $p.Start() | Out-Null
        $stdout = $p.StandardOutput.ReadToEnd()
        $stderr = $p.StandardError.ReadToEnd()
        $p.WaitForExit()
        $p | Add-Member "commandTitle" $commandTitle
        $p | Add-Member "stdout" $stdout
        $p | Add-Member "stderr" $stderr
    }
    Catch {
    }
    $p
}

0

İşte başka bir powershell işleminden çıktı almanın harika bir yolu:

start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'; import-clixml out.xml
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.