Mar*_*cki 3 powershell cmdlets
我有一个管道函数,它在begin块中分配一些需要在最后处理的资源.我已尝试在end块中执行此操作但在函数执行中止时不会调用它,例如ctrl+ c.
如何修改以下代码以确保$sw始终处置:
function Out-UnixFile([string] $Path, [switch] $Append) {
<#
.SYNOPSIS
Sends output to a file encoded with UTF-8 without BOM with Unix line endings.
#>
begin {
$encoding = new-object System.Text.UTF8Encoding($false)
$sw = new-object System.IO.StreamWriter($Path, $Append, $encoding)
$sw.NewLine = "`n"
}
process { $sw.WriteLine($_) }
# FIXME not called on Ctrl+C
end { $sw.Close() }
}
Run Code Online (Sandbox Code Playgroud)
编辑:简化功能
不幸的是,没有好的解决方案.确定性清理似乎是PowerShell中的明显遗漏.它可以像引入一个cleanup总是被调用的新块一样简单,无论管道如何结束,但是,即使版本5似乎也没有提供任何新东西(它引入了类,但没有清理机制).
也就是说,有一些不那么好的解决方案.最简单的,如果你枚举$input变量而不是使用begin/ process/ end你可以使用try/ finally:
function Out-UnixFile([string] $Path, [switch] $Append) {
<#
.SYNOPSIS
Sends output to a file encoded with UTF-8 without BOM with Unix line endings.
#>
$encoding = new-object System.Text.UTF8Encoding($false)
$sw = $null
try {
$sw = new-object System.IO.StreamWriter($Path, $Append, $encoding)
$sw.NewLine = "`n"
foreach ($line in $input) {
$sw.WriteLine($line)
}
} finally {
if ($sw) { $sw.Close() }
}
}
Run Code Online (Sandbox Code Playgroud)
这有一个很大的缺点,即你的函数将保持整个管道,直到一切都可用(基本上整个函数被视为一个大块end),如果你的函数是为了处理大量输入,这显然是一个交易破坏者.
第二种方法是坚持使用begin/ process/ end和手动过程控制-C作为输入,因为这是真正的问题的位.但绝不是唯一有问题的位,因为在这种情况下你也想要处理异常 - end基本上没有用于清理的目的,因为只有在成功处理整个管道时才会调用它.这需要一个邪恶的组合trap,try/ finally和标志:
function Out-UnixFile([string] $Path, [switch] $Append) {
<#
.SYNOPSIS
Sends output to a file encoded with UTF-8 without BOM with Unix line endings.
#>
begin {
$old_treatcontrolcasinput = [console]::TreatControlCAsInput
[console]::TreatControlCAsInput = $true
$encoding = new-object System.Text.UTF8Encoding($false)
$sw = new-object System.IO.StreamWriter($Path, $Append, $encoding)
$sw.NewLine = "`n"
$end = {
[console]::TreatControlCAsInput = $old_treatcontrolcasinput
$sw.Close()
}
}
process {
trap {
&$end
break
}
try {
if ($break) { break }
$sw.WriteLine($_)
} finally {
if ([console]::KeyAvailable) {
$key = [console]::ReadKey($true)
if (
$key.Modifiers -band [consolemodifiers]"control" -and
$key.key -eq "c"
) {
$break = $true
}
}
}
}
end {
&$end
}
}
Run Code Online (Sandbox Code Playgroud)
虽然详细,但这是我能提出的最简单的"正确"解决方案.它确实通过扭曲来确保Control-C状态正确恢复,并且我们从不尝试捕获异常(因为PowerShell在重新抛出它们时很糟糕); 如果我们不关心这些细节,解决方案可能会稍微简单一些.我甚至不会尝试就性能发表声明.:-)
如果有人有关于如何改进这一点的想法,我会全力以赴.显然,检查Control-C可以考虑到函数,但除此之外,似乎很难使它更简单(或至少更具可读性),因为我们被迫使用begin/ process/ end模具.
| 归档时间: |
|
| 查看次数: |
704 次 |
| 最近记录: |