以字符串而不是对象形式输出结果的命令:
ls | Out-String -Stream
Run Code Online (Sandbox Code Playgroud)
输出:
Directory: C:\MyPath\dir1
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2022-01-22 5:34 PM 0 1.txt
-a--- 2022-01-22 5:34 PM 0 2.txt
-a--- 2022-01-22 5:34 PM 0 3.txt
Run Code Online (Sandbox Code Playgroud)
我尝试使用函数获得相同的结果:
function f {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
$Content
)
process {
$Content | Out-String -Stream
}
}
ls | f
Run Code Online (Sandbox Code Playgroud)
但是,每个项目的输出都是分开的:
Directory: C:\MyPath\dir1
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2022-01-22 5:34 PM 0 1.txt
Directory: C:\MyPath\dir1
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2022-01-22 5:34 PM 0 2.txt
Directory: C:\MyPath\dir1
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2022-01-22 5:34 PM 0 3.txt
Run Code Online (Sandbox Code Playgroud)
如何使用该函数获得与第一个命令相同的结果?
San*_*zon 10
正如亚伯拉罕在评论中指出的那样,您可以首先捕获来自管道的所有对象,然后将对象作为流输出输出,以便在控制台中正确显示。
值得注意的是,下面显示的两个示例都不是真正的“流函数”,正如mklement0在他的有用答案中指出的那样,这两个函数首先收集来自管道的所有输入,然后立即将对象作为字符串流输出,而不是逐个对象。
function f {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[object] $Content
)
begin { $output = [System.Collections.Generic.List[object]]::new() }
process { $output.Add($Content) }
end { $output | Out-String -Stream }
}
Run Code Online (Sandbox Code Playgroud)
作为上面发布的高级函数的替代方案,下面的示例也可以工作,因为自动变量$input如何通过枚举函数的所有输入的集合来在end块中工作:
function f { $input | Out-String -Stream }
Run Code Online (Sandbox Code Playgroud)
我认为这个问题需要一个反问:
\n我怀疑你要么有一个罕见的需求,要么没有完全理解PowerShell Pipeline
\n一般来说,我会避免Out-String在 pipeline\xc2\xb9 的中间使用,因为虽然可以从 pipeline 的中间调用它,但它已经格式化了输出,类似于Format-Table通常在流末尾完成的操作。关键还在于,如果不知道接下来会发生什么,就很难预先确定列的宽度。
Out-String -Stream: 来弥补Select-String使用非字符串输入对象的无用行为。事实上,PowerShell 附带了代理函数 oss,它封装了Out-String -Stream- 另请参阅:GitHub 问题 #10726和他的有用答案。该Out-String -Stream参数对此也无济于事,因为它所做的只是将多行字符串分解为单独的字符串:
\n\n默认情况下,
\nOut-String输出单个字符串,其格式与您在控制台中看到的一样,包括任何空白标题或尾随换行符。Stream 参数可以Out-String逐行输出。唯一的例外是多行字符串。在这种情况下,Out-String仍会将字符串输出为单个多行字符串。
(ls | Out-String -Stream).count\n8\n\n(ls | Out-String -Stream)[3]\nMode LastWriteTime Length Name\nRun Code Online (Sandbox Code Playgroud)\n但是,如果您确实需要将PowerShell 对象(针对流式传输进行了优化)贬值为字符串(不会造成阻塞)管道),您实际上可以这样做:
\nfunction f {\n [CmdletBinding()] param ([Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]$Content)\n begin { $First = $True }\n process {\n if ($First) { $Content |Out-String -Stream |Select-Object -SkipLast 1 }\n else { $Content |Out-String -Stream |Select-Object -Skip 5 |Select-Object -SkipLast 1 }\n $First = $False\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n它显示了您正在尝试做的事情,但正如所说的,如果您没有很好的理由,我真的建议您不要这样做。执行此操作并尊重管道的通常方法是将Out-String外部 cmdlet 放置在流的末尾:
ls |f |Out-String -Stream\nRun Code Online (Sandbox Code Playgroud)\n
正如您所经历的,调用Out-String -Stream 每个输入对象并不能按预期工作,因为除了效率低之外,单独格式化每个对象总是会重复(表)格式输出中的标头。
圣地亚哥的有用答案中的解决方案是有效的,但缺点是在处理之前首先收集所有管道输入,如以下示例所示:
function f { $input | Out-String -Stream }
# !! Output doesn't appear until after the sleep period.
& { Get-Item $PROFILE; Start-Sleep 2; Get-Item $PROFILE } | f
Run Code Online (Sandbox Code Playgroud)
注意:输出时序是一方面,另一个方面是内存使用;在给定的用例中,这两个方面都可能重要,也可能不重要。
要以流方式包装 cmdlet 调用(其中对象可用时对其进行处理) ,您需要一个利用可步进管道的所谓代理(包装器)函数。
事实上,PowerShell 附带了一个oss函数,该函数恰好是 的代理函数Out-String -Stream,作为后者的便捷快捷方式:
# Streaming behavior via the built-in proxy function oss:
# First output object appears *right away*.
& { Get-Item $PROFILE; Start-Sleep 2; Get-Item $PROFILE } | oss
Run Code Online (Sandbox Code Playgroud)
代理函数的定义oss(wraps Out-String -Stream);函数体通过以下方式获得$function:oss:
function oss {
[CmdletBinding()]
param(
[ValidateRange(2, 2147483647)]
[int]
${Width},
[Parameter(ValueFromPipeline = $true)]
[psobject]
${InputObject})
begin {
$PSBoundParameters['Stream'] = $true
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Out-String', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = { & $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
process {
$steppablePipeline.Process($_)
}
end {
$steppablePipeline.End()
}
<#
.ForwardHelpTargetName Out-String
.ForwardHelpCategory Cmdlet
#>
}
Run Code Online (Sandbox Code Playgroud)
笔记:
主体的大部分是生成的代码 - 只有包装的 cmdlet 的名称 - Out-String- 及其参数 - -Stream- 特定于函数。
请参阅此答案以获取更多信息。
| 归档时间: |
|
| 查看次数: |
1144 次 |
| 最近记录: |