Powershell の Bash のプロセス置換に相当するもの

Powershell の Bash のプロセス置換に相当するもの

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

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

また、パイプ ベースのソリューションではなく、コマンド ラインの途中に挿入できるものを探しています。

答え1

この答えはあなたの為ではない、もしあなたが
次の場合: - 外部CLIを使用する必要がほとんどない(一般的には努力する価値がある) - PowerShellネイティブコマンドは一緒に使用する方がはるかに優れているため、そのような機能は必要ありません。
- Bashのプロセス置換に精通していない。
この回答あなたのためです次のような場合は、
特にスクリプトの作成中に、外部 CLI を頻繁に使用します (習慣によるか、(適切な) PowerShell ネイティブの代替手段がないため)。
- Bash のプロセス置換の機能に慣れており、その価値を理解しています。
-アップデート: PowerShellがUnixプラットフォームでもサポートされるようになったため、この機能への関心が高まっています。GitHubでこの機能リクエストこれは、PowerShell がプロセス置換に似た機能を実装していることを示唆しています。

Unixの世界では、Bash/Ksh/Zshでは、プロセス置換コマンド出力を一時的なものとして扱う機能を提供しますファイル自分自身の後始末をする。例えばcat <(echo 'hello')、コマンドcatの出力をecho一時ファイルのパスを含むコマンド出力

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

PowerShellで機能をエミュレートするのは面倒ですただし、頻繁に必要になる場合は、それだけの価値があるかもしれません。

cfスクリプトブロックを受け入れ、ブロックを実行し、その出力を必要に応じて作成された一時ファイルに書き込み、一時ファイルのパスを返すという関数を想像してください。; 例:

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

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

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

残念ながら、自動掃除プロセス置換の側面は直接エミュレートされるため、明示的なクリーンアップ呼び出しが必要です; クリーンアップは、cf 議論なし:

  • のために相互の作用使用して、あなたできるprompt次のように関数にクリーンアップ呼び出しを追加してクリーンアップを自動化します(prompt関数はプロンプトを返しますですが、Bash の変数と同様に、プロンプトが表示されるたびにバックグラウンドでコマンドを実行するためにも使用できます。任意の$PROMPT_COMMAND対話型セッションで使用できるようにするには、PowerShell プロファイルに次の内容とcf以下の定義を追加します。

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

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

注記: (PSv3+を必要とする)を使用してNew-Module、関数をダイナミックモジュール関数パラメータと渡されたスクリプト ブロック内で参照される変数の間に変数の競合が発生しないことを保証します。

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

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

答え2

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

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

二重引用符で囲むと、お気づきのとおり、`"$(...)" は文字列を返すだけです。

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

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

関連情報