Powershell管道会导致爆炸性内存使用

Seb*_*zzz 6 powershell batch-file

我目前正在Powershell中编写一个脚本,允许将SVN存储库中的文件夹复制到另一个文件夹,同时保留历史记录.这种命令的一个例子是:

svnadmin.exe dump $FromRepoPath `
    | svndumpfilter.exe include --drop-empty-revs --renumber-revs --preserve-revprops $Folder `
    | svnadmin.exe load --ignore-uuid $ToRepoPath
Run Code Online (Sandbox Code Playgroud)

这会在Powershell中导致非常高的内存使用量.似乎Powershell首先执行svnadmin.exe并从SVN admin缓冲stdout,然后执行svndumpfilter并缓冲输出并最终执行svnadmin.exe.

我可以通过创建一个单独的批处理文件来解决它:

@echo off
svnadmin.exe dump %1 | svndumpfilter.exe include --drop-empty-revs --renumber-revs --preserve-revprops %2 | svnadmin.exe load --ignore-uuid %3
Run Code Online (Sandbox Code Playgroud)

然后从Powershell调用它:

cmd.exe /c "SvnHelper.cmd $FromRepoPath $Folder $ToRepoPath"
Run Code Online (Sandbox Code Playgroud)

但这感觉就像一个讨厌和不必要的解决方法.

有没有办法告诉Powershell在管道而不是缓冲时直接传递?

tby*_*tby 5

它不是被缓冲的输出,而是任何外部进程的输入。您可以使用如下函数验证行为:

function Read-Pipeline {
[cmdletbinding()]
param ([Parameter(Mandatory = $true, ValueFromPipeline=$true)] $inp)
    Begin {}
    Process {Write-Verbose $inp ; Return $inp}
    End {}
}
Run Code Online (Sandbox Code Playgroud)

如果您随后运行:

.\LongRunning.exe | Read-Pipeline -Verbose | .\Other.exe
Run Code Online (Sandbox Code Playgroud)

您将看到 LongRunning.exe 的详细输出,但 Other.exe 在其管道关闭之前不会运行。如果你这样做:

.\LongRunning.exe | Read-Pipeline -Verbose | Write-Host
Run Code Online (Sandbox Code Playgroud)

您将看到交替的详细/控制台输出行,没有缓冲,因为输入没有跨越进程边界。

这些都没有真正帮助你。您可以通过回退到 .NET 来启动进程并手动将 STDOUT 复制到 STDIN [1] 来解决此问题,但工作量很大,回报却很少。最简单的方法是将命令传递给 CMD,例如:

& cmd.exe "/C svnadmin.exe dump $FromRepoPath ^| svndumpfilter.exe include --drop-empty-revs --renumber-revs --preserve-revprops $Folder ^| svnadmin.exe load --ignore-uuid $ToRepoPath
Run Code Online (Sandbox Code Playgroud)

[1] http://sushihangover.blogspot.com/2012/01/powershell-piping-standard-output-to.html