Bash<(..)
用于进程替换。Powershell 的等价物是什么?
我知道有$(...)
,但它返回一个字符串,同时<(..)
返回一个外部命令可以读取的文件,这是它所期望的。
我也不是在寻找基于管道的解决方案,而是我可以在命令行中间粘贴的东西。
如果您满足以下条件,则此答案不适合您:
- 很少(如果有的话)需要使用外部 CLI(这通常值得努力 - PowerShell 原生命令一起发挥得更好,并且不需要这样的功能)。
- 不熟悉 Bash 的进程替换。
如果您满足以下条件,那么这个答案适合您:
- 经常使用外部 CLI(无论是出于习惯还是由于缺乏(良好的)PowerShell 原生替代方案),尤其是在编写脚本时。
- 习惯并欣赏 Bash 的进程替换可以做什么。
-更新:现在 Unix 平台也支持 PowerShell,此功能越来越受关注 - 请参阅GitHub 上的此功能请求,这表明 PowerShell 实现了类似于进程替换的功能。
在 Unix 世界中,在 Bash/Ksh/Zsh 中,进程替换提供了将命令输出视为一个可以自行清理的临时文件的功能;例如cat <(echo 'hello')
,where将命令cat
的输出视为包含命令输出的临时文件的路径。echo
虽然 PowerShell 本机命令并不真正需要此类功能,但在处理外部 CLI时它会很方便。
在 PowerShell 中模拟该功能很麻烦,但如果您发现自己经常需要它,那么可能是值得的。
想象一个名为的函数cf
,它接受脚本块,执行该块并将其输出写入临时文件。根据需要创建文件,并返回临时值。文件的路径;例如:
findstr.exe "Windows" (cf { Get-ChildItem c:\ }) # findstr sees the temp. file's path.
Run Code Online (Sandbox Code Playgroud)
这是一个简单的示例,并不能很好地说明对此类功能的需求。也许更令人信服的场景是用于psftp.exe
SFTP 传输:其批量(自动)使用需要提供包含所需命令的输入文件,而此类命令可以轻松地动态创建为字符串。
为了尽可能广泛地与外部实用程序兼容,温度。默认情况下,文件应使用不带 BOM(字节顺序标记)的UTF-8编码,但如果需要,您可以请求带有 的 UTF-8 BOM。-BOM
不幸的是,进程替换的自动清理方面无法直接模拟,因此需要显式清理调用;清理是通过cf
不带参数调用来执行的:
对于交互式使用,您可以通过向函数添加 cleanup 调用来自动化清理prompt
,如下所示(该prompt
函数返回提示字符串,但也可用于在每次显示提示时执行幕后命令,类似于 Bash 的$PROMPT_COMMAND
多变的); 为了在任何交互式会话中可用,请将以下内容以及cf
以下定义添加到您的 PowerShell 配置文件中:
"function prompt { cf 4>`$null; $((get-item function:prompt).definition) }" |
Invoke-Expression
Run Code Online (Sandbox Code Playgroud)为了在 script 中使用,为了确保执行清理,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.
}
Run Code Online (Sandbox Code Playgroud)
这是实现:高级函数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
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意可以选择指定输出[文件]路径和/或文件扩展名。
当未用双引号括起来时,$(...)
返回一个 PowerShell 对象(或者更确切地说,所包含的代码返回的任何内容),首先评估所包含的代码。这应该适合您的目的(“某些东西 [I] 可以粘在命令行中间”),假设命令行是 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 #>
Run Code Online (Sandbox Code Playgroud)
正如您所注意到的,当用双引号引起来时,“$(...)”将仅返回一个字符串。
通过这种方式,如果您想直接在一行上插入文件的内容,您可以使用如下所示的内容:
Invoke-Command -ComputerName (Get-Content C:\Temp\Files\new1.txt) -ScriptBlock {<# something #>}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4467 次 |
最近记录: |