gpt4 book ai didi

Powershell 函数处置或中止处理程序

转载 作者:行者123 更新时间:2023-12-03 14:15:39 29 4
gpt4 key购买 nike

我有一个管道函数,它在 begin block 中分配一些需要在最后处理的资源。我已经尝试在 end block 中执行此操作,但是当函数执行被中止时它不会被调用,例如 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() }
}

编辑:简化函数

最佳答案

不幸的是,对此没有好的解决方案。确定性清理似乎是 PowerShell 中的一个明显遗漏。它可以像引入一个新的 cleanup block 一样简单,无论管道如何结束,它总是被调用,但唉,即使是版本 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() }
}
}

这有一个很大的缺点,即您的函数将占用整个管道,直到一切可用(基本上整个函数被视为一个大的 end block ),如果您的函数旨在处理大量输入。

第二种方法是坚持使用 begin/process/end 并手动处理 Control-C 作为输入,因为这确实是有问题的位。但决不是 唯一 有问题的位,因为在这种情况下您还想处理异常 - end 基本上对于清理目的是无用的,因为它仅在以下情况下被调用整个管道已成功处理。这需要 traptry/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
}
}

虽然冗长,但这是我能想到的最短的“正确”解决方案。它确实通过扭曲来确保正确恢复 Control-C 状态,并且我们从不 try catch 异常(因为 PowerShell 不擅长重新抛出它们);如果我们不关心这些细节,解决方案可能会稍微简单一些。我什至不打算尝试对性能发表声明。 :-)

如果有人对如何改进它有想法,我会洗耳恭听。显然,可以将对 Control-C 的检查分解为一个函数,但除此之外,似乎很难使其更简单(或至少更具可读性),因为我们被迫使用 begin/处理/结束模具。

关于Powershell 函数处置或中止处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28522507/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com