PowershellはBashのプロセス置換に相当します


14

Bashに<(..)はプロセス置換があります。Powershellと同等のものは何ですか?

があることは知って$(...)いますが、文字列を<(..)返しますが、外側のコマンドが読み取ることができるファイルを返します。

また、パイプベースのソリューションも探していませんが、コマンドラインの途中に貼り付けることができます。


3
そのようなことはありませんが、間違っていると証明されるのは興味深いでしょう。
ゾレダチェ

4
あなたはそれがどのように使用されることを期待するかのモックアップ例を与えることができますか?$(... | select -expandproperty objectyouwanttopass)が単一の置換の場合に適しているのではないかと思っています。
アンディ

2
PowerShellでは、$()は副次式演算子です。次のように使用できますWrite-Output "The BITS service is $(Get-Service bits | select -ExpandProperty Stauts)"。最初に変数にロードせずにBITSサービスのステータスを取得します。プロセス置換を見てみると、これはまったく同じではありませんが、それはまだあなたが直面している問題が解決するかもしれない
mortenya

@Andy:この機能は、ファイル名オペランドを必要とする外部ユーティリティで役立ちます。一例ではあるSFTP転送のために:そのオプションは、サーバー上で実行するコマンドを提供する必要がありますファイルを経由してあなただけ実行したい場合は、言って、不便です、。PowerShellにプロセス置換がある場合、次のようなことができるでしょう。psftp.exe-bmget *psftp.exe -l user -p password somehost -b <( "mget *" )
mklement

回答:


4

この答えはあなたのためではありません
-まれに、外部CLIを使用する必要はめったにありません(一般的に努力する価値があります-PowerShellネイティブコマンドは一緒に非常によく機能し、そのような機能は必要ありません)。
-Bashのプロセス置換に慣れていない。
この回答、次の場合に
役立ちます。-頻繁に外部CLIを使用する(習慣ではないか、(良い)PowerShellネイティブの代替がないため)、特にスクリプトを作成するとき。
-Bashのプロセス置換ができることに慣れており、感謝しています。
- 更新:PowerShellがUnixプラットフォームでもサポートされるようになったため、この機能への関心が高まっています。GitHubでこの機能のリクエストをご覧ください、PowerShellがプロセス置換に類似した機能を実装することを示唆しています。

Unixの世界のBash / Ksh / Zshでは、プロセス置換は、コマンド出力を、それ自体をクリーンアップする一時ファイルであるかのように扱うことを提供します。例えばcat <(echo 'hello')コマンド出力を含む一時ファイルのパスとしてコマンドcatからの出力を見ますecho

PowerShellネイティブのコマンドにはこのような機能は実際には必要ありませんが、外部CLIを扱う場合には便利です

PowerShellでこの機能をエミュレートするのは面倒ですが、頻繁に必要な場合は価値があります。

cfスクリプトブロックを受け入れ、ブロックを実行し、その出力をtempに書き込むという名前の関数を想像してください。ファイルはオンデマンドで作成され、一時ファイルを返します。ファイルのパス ; 例えば:

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

これは、このような機能の必要性をうまく説明していない単純な例です。おそらくより説得力のあるシナリオは、psftp.exeSFTP転送の使用です。バッチ(自動)使用には、目的のコマンドを含む入力ファイルを提供する必要がありますが、そのようなコマンドはオンザフライで文字列として簡単に作成できます。

外部ユーティリティと可能な限り広く互換性を保つため、temp。ファイルはデフォルトでBOM(バイトオーダーマーク)なしのUTF-8エンコーディング使用する必要がありますが-BOM、必要に応じてUTF-8 BOMをリクエストできます。

残念ながら、プロセス置換の自動クリーンアップの側面は直接エミュレートできないため、明示的なクリーンアップコールが必要です。クリーンアップは、cf 引数なしで呼び出すことにより実行されます

  • 以下のための対話型の使用、あなたができるあなたにクリーンアップコールを追加することにより、クリーンアップを自動化するprompt(次のように機能prompt関数はプロンプトを返す文字列を、だけでなく、舞台裏を実行するために使用することができますがバッシュのに似たプロンプトが表示されるたびに、コマンド$PROMPT_COMMAND変数); インタラクティブセッションで使用できるcfようにするには、PowerShellプロファイルに次の定義と以下の定義を追加します。

    "function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
      Invoke-Expression
  • スクリプトで使用する場合クリーンアップが実行されるようにするには、使用するブロック(場合によってはcfスクリプト全体)をtry/ finallyブロックでラップする必要があり、cf引数なしでクリーンアップのために呼び出されます。

# 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.
}

ここだ実装は:高度な機能ConvertTo-TempFileとその簡潔別名、cf

:の使用によりNew-ModulePSv3 +が必要になり、動的モジュールを介して関数が定義され、関数パラメーターと、渡されたスクリプトブロック内で参照される変数の間に変数の競合がなくなります。

$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
    }
  }
}

オプションで出力[ファイル]パスやファイル拡張子を指定できることに注意してください。


これを行う必要があるという考えは、せいぜい疑わしく、単純にPowerShellを使用したくないために、物事をより難しくしているだけです。
ジムB

1
@JimB:私は個人的にそれをpsftp.exeで使用しています。PowerShellですべてをネイティブに実行することが望ましい場合でも、それが常に可能であるとは限りません。PowerShellから外部CLIを呼び出すと、引き続き実行されます。メモリ内または別のコマンドで(より)簡単に作成できるファイル入力を必要とするCLIを繰り返し処理している場合は、この回答の関数を使用すると作業が楽になります。
mklement

じょうだんですか?いずれも必要ありません。パラメータ用のコマンドを含むファイルのみを受け入れるコマンドをまだ見つけていません。SFTPに関する限り、単純な検索では、PowerShellでFTPをネイティブに実行する2つの単純なアドインアセンブリが示されました。
ジムB

1
@JimB:建設的な方法でこの会話を続けたい場合は、トーンを変えてください。
mklement

2
@JimB GNU Diffutils diffは、あなたが夢中になっている場合にのみファイルで動作します。
パベル

2

二重引用符で囲まれていない場合$(...)、PowerShellオブジェクト(または、囲まれたコードによって返されるもの)を返し、囲まれたコードを最初に評価します。これは、コマンドラインがPowerShellであると仮定して、目的に適している必要があります(「[I]がコマンドラインの中央に固定できる」)。

これをテストするにはGet-Member、さまざまなバージョンをにパイプするか、直接出力します。

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 #>

お気づきのように、二重引用符で囲むと、「 "$(...)」は文字列を返します。

このようにして、たとえばファイルの内容を行に直接挿入したい場合、次のようなものを使用できます。

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

これは素晴らしい答えです!!
GregL

あなたが説明しているの、Bashのプロセス置換に相当するものではありません。プロセス置換は、ファイル名オペランドを必要とするコマンドで使用するために設計されています。つまり、プロセス置換で囲まれたコマンドからの出力は、大まかに言って一時ファイルに書き込まれ、そのファイルのパスが返されます。さらに、ファイルの存在は、プロセス置換が含まれるコマンドに限定されます。PowerShellにそのような機能があれば、次のようなものが機能すると期待できますGet-Content <(Get-ChildItem)
。– mklement

私が間違っている場合は修正してください、これはあなたが探しているものではありませんが、Get-ChildItem | Get-Content完全にうまく機能しませんか?またはGet-Content (Get-ChildItem).FullName、同じ効果を試すこともできますか?別のスクリプトアプローチの影響を完全に受けたビューからこれにアプローチしている可能性があります。
ジェームズラスキン

1
はい、PowerShellの領域では、この機能は必要ありません。ファイル入力を必要とする外部CLIでの使用にのみ関心があり、そのようなファイルのコンテンツは(PowerShell)コマンドで簡単に構築できます。実際の例については、質問に対する私のコメントを参照してください。このような機能は必要ないかもしれませんが、外部CLIを頻繁に呼び出す必要がある人にとっては興味深いものです。少なくとも、OPが具体的に求めていることとは対照的に、PowerShellのやり方を示していること、そしてなぜそうしているの説明することで、答えの前書きをする必要があります。
mklement
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.