Powershell'in Bash'in süreç ikamesine eşdeğer olması


14

Bash <(..)proses ikamesi için var. Powershell eşdeğeri nedir?

Ben biliyorum $(...), ama bir dize <(..)döndürür, dış komut okuyabilir bir dosya döndürür ise, beklediği budur.

Ayrıca boru tabanlı bir çözüm aramıyorum, ama komut satırının ortasına yapışabileceğim bir şey.


3
afaik böyle bir şey yok, ama yanlış kanıtlanması ilginç olurdu.
Zoredache

4
Nasıl kullanılmasını beklediğinize dair bir örnek verebilir misiniz? $ (... | select -expandproperty objectyouwanttopass) öğesinin tek bir ikame durumuna uygun olup olmadığını merak ediyorum.
Andy

2
PowerShell $ () alt ifade operatörüdür, bunu şu şekilde kullanabilirsiniz: Write-Output "The BITS service is $(Get-Service bits | select -ExpandProperty Stauts)"BITS hizmetinin durumunu bir değişkene yüklemeden almak için. Süreç
Değişimi'ne

@Andy: Bu özellik, dosya adı işlenenleri gerektiren harici yardımcı programlara yardımcı olur . SFTP aktarımlarına bir örnek : seçeneği, sunucuda çalıştırmak için komutlar sağlamanızı gerektirir ; bu, yalnızca çalıştırmak istiyorsanız, örneğin, rahatsız edici bir dosyadır . PowerShell'de işlem değişikliği olsaydı, benzer bir şey yapabilirsiniz . psftp.exe-bmget *psftp.exe -l user -p password somehost -b <( "mget *" )
mklement

Yanıtlar:


4

Aşağıdaki durumlarda bu cevap sizin için DEĞİLDİR :
- nadiren, eğer varsa, harici CLI'leri kullanmanız gerekir (genellikle uğraşmaya değer - PowerShell yerel komutları birlikte çok daha iyi oynar ve böyle bir özelliğe ihtiyaç duymazlar).
- Bash'in süreç ikamesine aşina değil.
Bu cevap size IS : eğer,
sık sık özellikle senaryo yazımı sırasında, (ister PowerShell yerli alternatifleri) iyiliği (olmaması alışkanlıktan veya bağlı) Dış CLIS kullanın -.
- Bash'in süreç ikamesinin neler yapabileceğini biliyor ve takdir ediyor.
- Güncelleme : Artık PowerShell, Unix platformlarında da desteklendiğine göre, bu özellik artan bir ilgi görüyor - GitHub'daki bu özellik isteğine bakınPowerShell'in, ikame işlemine benzer bir özellik uyguladığını gösterir.

Unix dünyasında, Bash / Ksh / Zsh içinde, bir süreç ikamesi bunun geçici sanki komut çıktısını tedavi teklifler olduğu dosya kendisini sonra temizler; örneğin cat <(echo 'hello'), komuttan çıktıyı komut çıktısını içeren geçici bir dosyanın yolu olarak catgörür .echo

PowerShell yerel komutlarının böyle bir özelliğe gerçek bir ihtiyacı olmamakla birlikte, harici CLI'lerle uğraşırken kullanışlı olabilir .

Özelliğin PowerShell'de taklit edilmesi zahmetlidir , ancak sık sık ihtiyaç duyduğunuzda buna değebilir.

cfBir komut dosyası bloğunu kabul eden, bloğu yürüten ve çıktısını bir sıcaklığa yazan bir işlev hayal edin. dosya istek üzerine oluşturulur ve temp döndürür. dosyanın yolu ; Örneğin:

 findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.

Bu, böyle bir özelliğe olan ihtiyacı iyi göstermeyen basit bir örnektir . Belki de daha ikna edici bir senaryo, psftp.exeSFTP transferleri için kullanımdır : toplu (otomatik) kullanımı , istenen komutları içeren bir girdi dosyası sağlamayı gerektirirken , bu tür komutlar anında bir dize olarak kolayca oluşturulabilir.

Mümkün olduğunca harici yardımcı programlarla olabildiğince uyumlu olması için sıcaklık. dosyada varsayılan olarak BOM (bayt sırası işareti) olmadan UTF-8 kodlaması kullanılmalıdır , ancak gerekirse UTF-8 BOM isteyebilirsiniz -BOM.

Ne yazık ki, işlem ikamelerinin otomatik temizleme yönü doğrudan taklit edilemez , bu nedenle açık bir temizleme çağrısı gereklidir ; temizleme, cf bağımsız değişkenler olmadan çağrılarak gerçekleştirilir :

  • İçin interaktif kullanım, sen yapabilirsiniz sizin için temizleme çağrısı ekleyerek temizleme otomatikleştirmek prompt(şöyle fonksiyonu promptişlevi istemi döndürür dize , aynı zamanda arkası sahneleri gerçekleştirmek için kullanılabilir Bash en benzer istemi görüntülendiğinde her zaman, komutları $PROMPT_COMMANDdeğişken); herhangi bir etkileşimli oturumda kullanılabilirlik cfiçin PowerShell profilinize aşağıdakilerin yanı sıra aşağıdakilerin tanımını da ekleyin:

    "function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
      Invoke-Expression
  • Komut dosyalarında kullanım için, temizlemenin gerçekleştirildiğinden emin olmak için, cf- muhtemelen tüm komut dosyasını kullanan bloğun try/ argümanına sarılması gerekir finally; burada cf, temizleme için bağımsız değişkenler çağrılmaz:

# Example
try {

  # Pass the output from `Get-ChildItem` via a temporary file.
  findstr.exe "Windows" (cf { Get-ChildItem c:\ })

  # cf() will reuse the existing temp. file for additional invocations.
  # Invoking it without parameters will delete the temp. file.

} finally {
  cf  # Clean up the temp. file.
}

İşte uygulanması gelişmiş fonksiyonu: ConvertTo-TempFileve özlü takma, cf:

Not : New-ModuleFonksiyonu dinamik bir modül üzerinden tanımlamak için PSv3 + 'yı gerektiren fonksiyonun kullanılması , fonksiyon parametreleri ile geçirilen kod bloğunda referans verilen değişkenler arasında değişken çakışmaların olmamasını sağlar.

$null = New-Module {  # Load as dynamic module
  # Define a succinct alias.
  set-alias cf ConvertTo-TempFile
  function ConvertTo-TempFile {
    [CmdletBinding(DefaultParameterSetName='Cleanup')]
    param(
        [Parameter(ParameterSetName='Standard', Mandatory=$true, Position=0)]
        [ScriptBlock] $ScriptBlock
      , [Parameter(ParameterSetName='Standard', Position=1)]
        [string] $LiteralPath
      , [Parameter(ParameterSetName='Standard')]
        [string] $Extension
      , [Parameter(ParameterSetName='Standard')]
        [switch] $BOM
    )

    $prevFilePath = Test-Path variable:__cttfFilePath
    if ($PSCmdlet.ParameterSetName -eq 'Cleanup') {
      if ($prevFilePath) { 
        Write-Verbose "Removing temp. file: $__cttfFilePath"
        Remove-Item -ErrorAction SilentlyContinue $__cttfFilePath
        Remove-Variable -Scope Script  __cttfFilePath
      } else {
        Write-Verbose "Nothing to clean up."
      }
    } else { # script block specified
      if ($Extension -and $Extension -notlike '.*') { $Extension = ".$Extension" }
      if ($LiteralPath) {
        # Since we'll be using a .NET framework classes directly, 
        # we must sync .NET's notion of the current dir. with PowerShell's.
        [Environment]::CurrentDirectory = $pwd
        if ([System.IO.Directory]::Exists($LiteralPath)) { 
          $script:__cttfFilePath = [IO.Path]::Combine($LiteralPath, [IO.Path]::GetRandomFileName() + $Extension)
          Write-Verbose "Creating file with random name in specified folder: '$__cttfFilePath'."
        } else { # presumptive path to a *file* specified
          if (-not [System.IO.Directory]::Exists((Split-Path $LiteralPath))) {
            Throw "Output folder '$(Split-Path $LiteralPath)' must exist."
          }
          $script:__cttfFilePath = $LiteralPath
          Write-Verbose "Using explicitly specified file path: '$__cttfFilePath'."
        }
      } else { # Create temp. file in the user's temporary folder.
        if (-not $prevFilePath) { 
          if ($Extension) {
            $script:__cttfFilePath = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName() + $Extension)
          } else {
            $script:__cttfFilePath = [IO.Path]::GetTempFilename() 
          }
          Write-Verbose "Creating temp. file: $__cttfFilePath"
        } else {
          Write-Verbose "Reusing temp. file: $__cttfFilePath"      
        }
      }
      if (-not $BOM) { # UTF8 file *without* BOM
        # Note: Out-File, sadly, doesn't support creating UTF8-encoded files 
        #       *without a BOM*, so we must use the .NET framework.
        #       [IO.StreamWriter] by default writes UTF-8 files without a BOM.
        $sw = New-Object IO.StreamWriter $__cttfFilePath
        try {
            . $ScriptBlock | Out-String -Stream | % { $sw.WriteLine($_) }
        } finally { $sw.Close() }
      } else { # UTF8 file *with* BOM
        . $ScriptBlock | Out-File -Encoding utf8 $__cttfFilePath
      }
      return $__cttfFilePath
    }
  }
}

İsteğe bağlı olarak bir çıktı [dosya] yolu ve / veya dosya adı uzantısı belirtebileceğinizi unutmayın.


Bunu yapmanız gerekeceği fikri en iyi ihtimalle şüphelidir ve sadece PowerShell'i kullanmak istememek için işleri daha da zorlaştırır.
Jim B

1
@JimB: Ben şahsen kullanıyorum psftp.exe, yazmamı sağlayan şey buydu. PowerShell'de her şeyi yerel olarak yapmak tercih edilmesine rağmen, bu her zaman mümkün değildir; harici CLI'leri PowerShell'den çağırmak yapar ve olmaya devam eder; kendinizi (daha fazla) bellekte / başka bir komutla kolayca oluşturulabilen dosya girişi gerektiren CLI'larla tekrar tekrar uğraşırsanız, bu yanıttaki işlev hayatınızı kolaylaştırabilir.
mklement

Şaka mı yapıyorsun? bunların hiçbirine gerek yok. Sadece parametreler için komutları olan dosyaları kabul eden bir komut bulmadım. SFTP kadar basit bir arama bana PowerShell'de FTP yapmak için 2 basit eklenti montajı gösterdi.
Jim B

1
@JimB: Bu sohbete yapıcı bir şekilde devam etmek istiyorsanız tonunuzu değiştirin.
mklement

2
@JimB GNU Diffutils diff, ilginizi çekmesi durumunda yalnızca dosyalarda çalışır.
Pavel

2

Çift tırnak içine alınmadığında $(...), önce ekteki kodu değerlendirerek bir PowerShell Nesnesi (ya da daha doğrusu, ekli kod tarafından döndürülen her şeyi döndürür) döndürür. Komut satırının PowerShell olduğunu varsayarak, bu sizin amaçlarınız için uygun olmalıdır ("[I] komut satırının ortasına yapışabilir").

Bunu, çeşitli sürümleri boruya Get-Memberbağlayarak hatta doğrudan çıktı olarak test edebilirsiniz .

PS> "$(ls C:\Temp\Files)"
new1.txt new2.txt

PS> $(ls C:\Temp\Files)


    Directory: C:\Temp\Files


Mode                LastWriteTime         Length Name                                                                      
----                -------------         ------ ----                                                                      
-a----       02/06/2015     14:58              0 new1.txt                                                                  
-a----       02/06/2015     14:58              0 new2.txt   

PS> "$(ls C:\Temp\Files)" | gm


   TypeName: System.String
<# snip #>

PS> $(ls C:\Temp\Files) | gm


   TypeName: System.IO.FileInfo
<# snip #>

Çift tırnak içine alındığında, fark ettiğiniz gibi, "$ (...)" bir dize döndürecektir.

Bu şekilde, örneğin bir dosyanın içeriğini doğrudan bir satıra eklemek isterseniz, şöyle bir şey kullanabilirsiniz:

Invoke-Command -ComputerName (Get-Content C:\Temp\Files\new1.txt) -ScriptBlock {<# something #>}

Bu harika bir cevap!
GregL

Açıkladığınız şey Bash'in süreç ikamesine eşdeğer değil . İşlem ikamesi, dosya adı işlenenleri gerektiren komutlarla kullanılmak üzere tasarlanmıştır ; yani, bir işlem ikamesinde bulunan bir komuttan elde edilen çıktı, gevşek bir şekilde, geçici bir dosyaya yazılır ve bu dosyanın yolu döndürülür; ek olarak, dosyanın varlığı, işlem ikamesinin bir parçası olduğu komuta dahil edilir. PowerShell böyle bir özelliğe sahip olsaydı, aşağıdakilerin çalışması için Get-Content <(Get-ChildItem)
beklersiniz

Lütfen yanılıyorsam beni düzeltin ve aradığınız şey bu değil, ama Get-ChildItem | Get-Contentmükemmel bir şekilde çalışmıyor mu? Yoksa Get-Content (Get-ChildItem).FullNameaynı etkiyi deneyebilir misiniz ? Buna başka bir senaryo yaklaşımından iyice etkilenmiş bir bakış açısıyla yaklaşıyor olabilirsiniz.
James Ruskin

1
Evet, PowerShell alanında bu özelliğe gerek yoktur; yalnızca dosya girişi gerektiren harici CLI'lerle ve bu tür dosyaların içeriğinin (PowerShell) komutuyla kolayca oluşturulduğu ilgi çekicidir . Gerçek dünya örneği için soru hakkındaki yorumuma bakın. Sen böyle bir özellik gerekir, ancak sık sık ilgi olduğunu dış CLIS aramak ihtiyacı olan insanlar için asla. Sen azından gösteren konum söyleyerek cevap önsöz de olmalıdır PowerShell şeyler yapmanın yolu - OP özellikle istediği şeylerin aksi - ve neden bu kadar yapıyoruz.
mklement
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.