Powershell相当于Bash的stream程replace

Bash有<(..)进程replace。 什么是Powershell的等价物?

我知道有$(...) ,但它返回一个string,而<(..)返回一个文件的外部命令可以读取,这是它所期望的。

我也没有寻找一个基于pipe道的解决scheme,但我可以坚持在命令行的中间。

这个答案不适合你 ,如果你:
– 很less,如果需要的话,需要使用外部CLI(这通常值得争取–PowerShell本地命令在一起发挥更好,并且不需要这样的特性)。
– 不熟悉Bash的stream程replace。
这个答案是给你的 ,如果你:
– 经常使用外部CLI(无论是出于习惯还是由于缺less(良好的)PowerShell本地备选scheme),特别是在编写脚本时。
– 习惯了Bash的stream程replace能做的事情。
更新 :现在PowerShell很快也会在Unix平台上得到支持,这个特性引起了越来越多的兴趣 – 在GitHub上看到这个特性请求 ,这表明PowerShell实现了类似于进程replace的function。

在Unix世界中,在Bash / Ksh / Zsh中, 进程replace是提供对待命令输出,就好像它是临时文件 ,它自己清理完毕; 例如cat <(echo 'hello') ,其中catecho命令的输出视为包含命令输出 的临时文件path

虽然PowerShell本机命令并不需要这种function,但在处理外部CLI时可能非常方便。

在PowerShell中模拟这个function非常麻烦 ,但如果你经常需要它,可能是值得的。

画一个名为cf的函数,接受一个脚本块,执行该块并将其输出写入一个temp。 文件按需创build,并返回临时文件。 文件的path ; 例如:

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

这是一个简单的例子,并没有很好地说明这种function的需要 也许更令人信服的情况是使用psftp.exe进行SFTP传输:批量(自动)使用需要提供包含所需命令的input文件 ,而这样的命令可以很容易地被创build为一个string。

为了尽可能广泛地与外部设备兼容, 文件应该默认使用没有BOM (字节顺序标记)的UTF-8编码,但是如果需要的话,您可以使用-BOM请求UTF-8 BOM。

不幸的是, 过程replace的自动清理方面不能直接模拟,所以需要明确的清理调用 ; 通过调用没有参数的 cf来执行清理。

  • 对于交互式使用,您可以通过将清除调用添加到prompt函数自动执行清除操作( prompt函数返回提示string ,但也可以在每次显示提示时执行幕后命令,类似到Bash的$PROMPT_COMMANDvariables); 为了在任何交互式会话中提供可用性,请将以下以及cf的定义添加到PowerShellconfiguration文件中:

     "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

注意 :使用需要PSv3 +的New-Module通过dynamic模块来定义函数,可以确保函数参数和传递的脚本块内引用的variables之间没有可变的冲突。

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

请注意,可以select指定输出[文件]path和/或文件扩展名。

当不用双引号括起来的时候, $(...)返回一个PowerShell对象(或者说,不pipe是否被封装的代码返回),首先评估封装的代码。 这应该适合您的目的(假设命令行是PowerShell,“我可以坚持在命令行中间”)。

您可以通过将各种版本输出到Get-Member ,甚至直接输出来testing。

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

当用双引号括起来的时候,你已经注意到了,`“$(…)”只是返回一个string。

这样,如果你想直接插入一个文件的内容,你可以使用如下的东西:

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