Çıkışı PowerShell'de harici bir işlemden bir değişkene nasıl yakalayabilirim?


159

Harici bir işlem çalıştırmak ve komut çıktısını PowerShell'de bir değişkene yakalamak istiyorum. Şu anda bunu kullanıyorum:

$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait

Komutun yürütüldüğünü doğruladım, ancak çıktıyı bir değişkene yakalamam gerekiyor. Bu sadece bir dosyaya yönlendirdiği için -RedirectOutput kullanamadığım anlamına gelir.


3
Her şeyden önce: Konsol uygulamalarını eşzamanlı olarak (tanım gereği harici) yürütmek için kullanmayın Start-Process- sadece herhangi bir kabukta olduğu gibi doğrudan çağırın ; zekâ etmek netdom /verify $pc /domain:hosp.uhhg.org. Bunu yapmak, uygulamayı çağrı konsolunun standart akışlarına bağlı tutar ve çıktısının basit atama ile yakalanmasını sağlar $output = netdom .... Aşağıda verilen cevapların çoğu Start-Processdolaysız olarak doğrudan icra lehine vazgeçmiştir .
mklement0

@ mklement0 belki biri şu -Credentialparametreyi kullanmak istiyorsa
CJBS

@CJBS Evet, birlikte çalıştırmak için farklı kullanıcı kimliğinin , kullanımını Start-Processşart - ama ancak o zaman (ve ayrı bir pencerede bir komutu çalıştırmak istiyorsanız). Ve bir bu durumda kaçınılmaz sınırlarının farkında olmalıdır: haricinde yakalama çıkışına Hayır yeteneği --olmayan araya ilave - Metin içinde dosyalar aracılığıyla -RedirectStandardOutputve -RedirectStandardError.
mklement0

Yanıtlar:


161

Denedin mi:

$OutputVariable = (Shell command) | Out-String


"=" Kullanarak bir değişkene atamaya çalıştım ama önce Out-String çıkış boru denemedim. Bunu deneyeceğim.
Adam Bertram

10
Burada neler olduğunu anlamıyorum ve işe yarayamıyorum. "Shell" bir powershell anahtar kelimesi mi? Yani aslında Start-Process cmdlet'ini kullanmıyoruz? Lütfen somut bir örnek verebilir misiniz (yani "Kabuk" ve / veya "komut" yerine gerçek bir örnek verin).
deadlydog

@deadlydog Shell CommandÇalıştırmak istediğiniz her şeyle değiştirin . Bu kadar basit.
JNK

1
@stej, haklısın. Esas olarak, yorumunuzdaki kodun yanıttaki koddan farklı bir işlevselliğe sahip olduğunu açıklığa kavuştum. Benim gibi yeni başlayanlar, böyle davranışlardaki ince farklılıklarla atılabilirler!
Sam

1
@Atique Aynı konu ile karşılaştım. Örneğin, -ibir çıktı dosyası belirtmeden seçeneği kullanırsanız, ffmpeg bazen stdout yerine stderr'e yazacaktır . Çıktıyı 2>&1diğer cevapların bazılarında anlatıldığı gibi yeniden yönlendirmek çözümdür.
jmbpiano

159

Not: Sorudaki komut Start-Process, hedef programın çıktısının doğrudan yakalanmasını önleyen kullanır . Genel olarak, konsol uygulamalarını eşzamanlı olarak yürütmek için kullanmayın Start-Process- yalnızca herhangi bir kabukta olduğu gibi doğrudan çağırın . Bunu yapmak, uygulamayı çağrı konsolunun standart akışlarına bağlı tutar ve çıktısının $output = netdom ...aşağıda açıklandığı gibi basit atamayla yakalanmasını sağlar .

Temel olarak , harici yardımcı programlardan çıktı yakalamak , PowerShell yerel komutlarıyla aynı şekilde çalışır ( harici araçların nasıl yürütüleceği hakkında bir tazeleme isteyebilirsiniz):

$cmdOutput = <command>   # captures the command's success stream / stdout

Harici bir program durumunda , programın çıktı satırlarını içeren bir dize dizisi anlamına gelen 1'den fazla çıktı nesnesi üretiyorsa bir nesne dizisi$cmdOutput aldığını unutmayın . Her zaman tek - potansiyel olarak çok satırlı - bir dize almak istiyorsanız ,<command>
$cmdOutput
$cmdOutput = <command> | Out-String


To yakalama bir değişkene çıktı ve ekrana yazdırmak :

<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed

Veya, <command>bir cmdlet veya gelişmiş işlevse , ortak parametre
-OutVariable/-ov
kullanabilirsiniz :

<command> -OutVariable cmdOutput   # cmdlets and advanced functions only

İle unutmayın -OutVariablediğer senaryolarda, aksine $cmdOutputolan her zaman bir koleksiyon sadece bile, bir nesne çıkıştır. Özellikle, dizi benzeri [System.Collections.ArrayList]türün bir örneği döndürülür. Bu tutarsızlığın tartışılması için bu GitHub sorununa
bakın .


Çıktısını yakalamak için birden fazla komutları , kullanıcı ya bir alt-ifade ( $(...)) veya (a komut blok çağrı { ... }ile) &ya da .:

$cmdOutput = $(<command>; ...)  # subexpression

$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.

$cmdOutput = . {<command>; ...} # script block with . - no child scope

Not olduğu ile önek genel ihtiyaç &(çağrı operatörü) Adını bireysel komuta / yol olduğunu alıntılanan - örneğin $cmdOutput = & 'netdom.exe' ...- se başına dış programlarla ilgili değildir (eşit PowerShell komut dosyaları için de geçerlidir), ama bir sözdizimi gereksinimi : PowerShell varsayılan olarak ifade modunda alıntılanmış bir dize ile başlayan bir ifadeyi ayrıştırırken , komutları (cmdlet'ler, harici programlar, fonksiyonlar, takma adlar) çağırmak için argüman modu gereklidir &.

Arasındaki temel fark, $(...)ve & { ... }/ . { ... }olduğu önceki toplar bellekte tüm giriş , bir bütün olarak dönmeden önce, ikincisi ise akımı tek-tek boru hattı işleme için uygun çıkış.


Yönlendirmeler de temelde aynı şekilde çalışır (ancak aşağıdaki uyarılara bakın):

$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)

Ancak, harici komutlar için aşağıdakilerin beklendiği gibi çalışması daha olasıdır:

$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.

Harici programlara özgü hususlar :

  • Harici programlar , çünkü PowerShell'in tip sistemi dışında çalıştıklarından, sadece başarı akışları (stdout) aracılığıyla dizeleri döndürürler .

  • Çıktı içeriyorsa 1'den fazla satır bir içine varsayılan bölünme yoluyla, PowerShell dizinin dizeleri . Daha doğrusu, çıktı satırları [System.Object[]]elemanları dizgiler ( [System.String]) olan bir tür dizide saklanır .

  • Eğer varsa çıkış bir olmak istiyorum tek potansiyel çok satırlı dize , boru içinOut-String :
    $cmdOutput = <command> | Out-String

  • Stderr'ı2>&1 başarı akışının bir parçası olarak yakalamak için stdout'a yönlendirmek uyarılarla birlikte gelir :

    • Yapmak için 2>&1birleştirme stdout ve stderr kaynağında , let cmd.exeyönlendirmeyi işlemek aşağıdaki deyimleri kullanarak,:
      $cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
      $cmdOutput = cmd /c <command> '2>&1' | Out-String # single string

      • cmd /ccmd.exekomut ile çağırır <command>ve <command>bittikten sonra çıkar .
      • Yeniden 2>&1yönlendirmenin cmd.exePowerShell tarafından yorumlanmak yerine iletilmesini sağlayan tek tırnak işaretlerini not alın .
      • Karıştığı unutmayın cmd.exearacı olduğunu onun karakterleri kaçan ve çevre değişkenleri genişletmek için kurallar PowerShell kendi şartlara ek olarak, varsayılan olarak, devreye giriyor; Eğer özel parametresini kullanabilirsiniz + PS v3 içinde --%(sözde durma-ayrıştırma sembolü PowerShell tarafından kalan parametrelerin yorumlanması kapatmak için) için hariç cmd.exegibi tarzı çevre değişken referanslar %PATH%.

      • Stdout ve stderr'ı bu yaklaşımla kaynağında birleştirdiğiniz için PowerShell'de stdout kaynaklı ve stderr kaynaklı satırları ayırt edemeyeceğinizi unutmayın ; bu ayrıma ihtiyacınız varsa, PowerShell'in kendi 2>&1yönlendirmesini kullanın - aşağıya bakın.

    • Hangi akışların hangi akıştan geldiğini bilmek için PowerShell 2>&1 yönlendirmesini kullanın :

      • Standart hataya çıkış olarak tespit edilir hata kayıtları ( [System.Management.Automation.ErrorRecord]böylece,), dize çıkış dizisi içerebilir karışımı içinde dizeleri (a stdout'u çizgisini temsil eden her bir dize) hata kaydı (bir şekilde hataları temsil eden her kaydı) . İstendiği gibi 2>&1, hem dizelerin hem de hata kayıtlarının PowerShell'in başarı çıktı akışı yoluyla alındığını unutmayın ).

      • Konsolda, hata kayıtları yazdırmak kırmızı ve 1 varsayılan olarak tek üretir çok satırlı bir cmdlet dışı sonlandırma hatası görüntüler edeceğini aynı biçimde, ekran; sonraki hata kayıtları yanı kırmızı yazdırmak, fakat sadece kendi hata yazdırmak mesajı bir üzerine, tek hat .

      • Çıkarılırken konsola , dizeleri tipik gel ilk neyse ki, ( "aynı anda" stdout / stderr çıkış satırı bir toplu arasından en az) hata kayıtları, ardından çıkış dizisinde, ama ne zaman yakalamak çıktı , olmadan alacağınız aynı çıkış sırasını kullanarak düzgün bir şekilde serpiştirilir2>&1 ; diğer bir deyişle: konsola çıktı alınırken , yakalanan çıktı harici komut tarafından stdout ve stderr satırlarının oluşturulma sırasını GÖSTERMEZ.

      • Eğer varsa bir bütün çıkış yakalamak tek ile dizeOut-String , PowerShell katacak fazladan satırlar bir hata kaydının dize gösterimi söz konusu lokasyon (gibi ekstra bilgiler içerdiğinden, At line:...) ve kategori ( + CategoryInfo ...); merakla, bu sadece ilk hata kaydı için geçerlidir .

        • Bu sorunu gidermek için, geçerli .ToString()yerine Borulama her çıkış nesnesine yöntemi Out-String:
          $cmdOutput = <command> 2>&1 | % { $_.ToString() };
          PS v3 + 'da şunları basitleştirebilirsiniz:
          $cmdOutput = <command> 2>&1 | % ToString
          (Bonus olarak, çıktı yakalanmazsa, konsola yazdırırken bile düzgün aralıklı çıktı üretir.)
      • Alternatif olarak, hata kayıtları filtrelemek dışarı ile PowerShell hatası akışına gönderebilirsinizWrite-Error (çıkış yakalanan değilse konsola yazdırırken prim olarak, bu bile düzgün serpiştirilmiş çıktı üretir):

$cmdOutput = <command> 2>&1 | ForEach-Object {
  if ($_ -is [System.Management.Automation.ErrorRecord]) {
    Write-Error $_
  } else {
    $_
  }
}

Çalıştırılabilir yolumu VE bununla ilgili argümanlarımı aldıktan sonra sonunda benim için işe yaradı, onları bir dizede fırlattı ve <komut> olarak ele aldım.
Dan

2
@Dan: PowerShell kendisi yorumladığı zaman <command>, sen olmamalıdır tek dizede çalıştırılabilir ve bağımsız değişkenlerini birleştirmek; cmd /csizin aracılığınızla çağırma ile bunu yapabilirsiniz ve bu mantıklı olup olmadığına bağlıdır. Hangi senaryoya atıfta bulunuyorsunuz ve minimal bir örnek verebilir misiniz?
mklement0

Çalışır: $ command = "c: \ mycommand.exe" + $ Args ..... $ output = cmd / c $ command '2> & 1'
Dan

1
@Dan: Evet, bu işe yarıyor, ancak işleçle dizenin ara değişkenine ve açık yapısına ihtiyacınız yok +; aşağıdakiler de çalışır: cmd /c c:\mycommand.exe $Args '2>&1'- PowerShell, öğelerin $Argsboşlukla ayrılmış bir dize olarak geçirilmesine dikkat eder , bu durumda splatting adı verilen bir özellik .
mklement0

Son olarak PS6.1 + 'da çalışan uygun bir cevap. Sosdaki sır gerçekten bir '2>&1'parçadır ve ()birçok komut dosyası yapma eğiliminde değildir.
not2qubit

24

Hata çıkışını da yeniden yönlendirmek istiyorsanız şunları yapmanız gerekir:

$cmdOutput = command 2>&1

Veya program adında boşluklar varsa:

$cmdOutput = & "command with spaces" 2>&1

4
2> & 1 ne anlama geliyor? '2 adlı komut çalıştır ve çıktısını 1 adlı komut çalıştır'?
Richard

7
"Standart hata çıktısını (dosya tanımlayıcı 2), standart çıktının (dosya tanımlayıcı 1) gittiği yere yeniden yönlendir" anlamına gelir. Temel olarak, normal ve hata mesajlarını aynı yere yönlendirir (bu durumda, stdout dosya gibi başka bir yere yönlendirilmezse konsol).
Giovanni Tirloni

11

Veya bunu deneyin. Çıktı değişken $ scriptOutput yakalar:

& "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null

$scriptOutput

7
-1, gereksiz yere karmaşık. $scriptOutput = & "netdom.exe" $params
CharlesB

8
Null değeri kaldırmak ve bu, hem kabuğa hem de bir değişkene aynı anda boru tesisatı yapmak için mükemmeldir.
ferventcoder

10

Başka bir gerçek hayat örneği:

$result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String

Bu örneğin bir yol (ortam değişkeniyle başlayan) içerdiğine dikkat edin. Tırnakların yolu ve EXE dosyasını çevrelemesi gerektiğini unutmayın, ancak parametreleri değil!

Not:& Komutun önündeki, tırnak işaretleri dışındaki karakteri unutmayın .

Hata çıkışı da toplanır.

Bu kombinasyonun çalışması biraz zaman aldı, bu yüzden paylaşacağımı düşündüm.


9

Cevapları denedim, ama benim durumumda ham çıktı alamadım. Bunun yerine bir PowerShell istisnasına dönüştürüldü.

Aldığım ham sonuç:

$rawOutput = (cmd /c <command> 2`>`&1)

2

Bu şey benim için çalıştı:

$scriptOutput = (cmd /s /c $FilePath $ArgumentList)

2

Çalışmak için aşağıdakileri aldım:

$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe"
$result = & invoke-Expression $Command1 | Out-String

$ sonuç size ihtiyacı olanı verir


1

Yapmaya çalıştığınız tek şey çıktıyı bir komuttan yakalamaksa, bu işe yarayacaktır.

Bunu , sistemde değişiklik yaptıktan sonra[timezoneinfo]::local bile, her zaman aynı bilgileri ürettiğinden, sistem saatini değiştirmek için kullanıyorum . Bu, zaman dilimindeki değişikliği doğrulayıp günlüğe kaydedebilmemin tek yoludur:

$NewTime = (powershell.exe -command [timezoneinfo]::local)
$NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append

Yani sistem değişkenlerini yeniden yüklemek için yeni bir PowerShell oturumu açmak zorundayım .

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.