如何在不破坏主脚本的情况下使用 Ctrl+C 停止子进程并将子进程输出流式传输到 PowerShell 中的主脚本?
How to stop a child process with Ctrl+C without breaking the main script and stream child output to the main script in PowerShell?
我正在编写 运行正在 Linux 上运行的 powershell 脚本。此脚本的目的是 运行 MS SQL 服务器并显示其输出,但是当用户按下 Ctrl+C 或发生某些错误,脚本获取数据文件夹的副本,然后退出。
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
BEGIN {
Write-Output "started $PSScriptRoot"
$currentErrorLevel = $ErrorActionPreference
# [console]::TreatControlCAsInput = $true
}
PROCESS {
try {
$ErrorActionPreference = 'SilentlyContinue';
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
$ErrorActionPreference = $currentErrorLevel;
Write-Error $_.Exception
}
}
End {
$ErrorActionPreference = $currentErrorLevel;
#do backup
Create-Backup "/opt/mssql/bin/data"
Write-Output "finishd!"
}
这个脚本有几个问题:
- 当我按下 Ctrl+C 时,它破坏了主脚本并且它永远不会到达
Create-Backup
部分脚本底部。
- 如果我删除
-Wait
那么脚本将不会显示 sql 日志输出
所以我的首选解决方案是 运行 带有 -Wait
参数的 sql 并防止 powershell 在我按下 Ctrl[=33= 后退出代码]+C,而是 Ctrl+C 关闭 sql 实例
所以我正在寻找一种方法来实现两者。
为简单起见,我假设您的函数只需要支持一个 单个 输入对象,因此我使用的是一个没有 begin
的简单函数体,process
和 end
块,相当于 只是 一个 end
块:
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
Write-Output "started $PSScriptRoot"
# No need to save the current value, because the modified
# value is local to the function and goes out of scope on
# exiting the function.
$ErrorActionPreference = 'SilentlyContinue';
try {
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
Write-Error $_.Exception
}
finally {
# This block is *always* called - even if Ctrl-C was used.
Create-Backup "/opt/mssql/bin/data"
# CAVEAT: If Ctrl-C was used to terminate the run,
# you can no longer produce *pipeline* input at this point
# (it will be quietly ignored).
# However, you can still output to the *host*.
Write-Host "finished!"
}
如果你真的需要支持多个输入对象,它会变得更复杂。
我正在编写 运行正在 Linux 上运行的 powershell 脚本。此脚本的目的是 运行 MS SQL 服务器并显示其输出,但是当用户按下 Ctrl+C 或发生某些错误,脚本获取数据文件夹的副本,然后退出。
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
BEGIN {
Write-Output "started $PSScriptRoot"
$currentErrorLevel = $ErrorActionPreference
# [console]::TreatControlCAsInput = $true
}
PROCESS {
try {
$ErrorActionPreference = 'SilentlyContinue';
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
$ErrorActionPreference = $currentErrorLevel;
Write-Error $_.Exception
}
}
End {
$ErrorActionPreference = $currentErrorLevel;
#do backup
Create-Backup "/opt/mssql/bin/data"
Write-Output "finishd!"
}
这个脚本有几个问题:
- 当我按下 Ctrl+C 时,它破坏了主脚本并且它永远不会到达
Create-Backup
部分脚本底部。 - 如果我删除
-Wait
那么脚本将不会显示 sql 日志输出
所以我的首选解决方案是 运行 带有 -Wait
参数的 sql 并防止 powershell 在我按下 Ctrl[=33= 后退出代码]+C,而是 Ctrl+C 关闭 sql 实例
所以我正在寻找一种方法来实现两者。
为简单起见,我假设您的函数只需要支持一个 单个 输入对象,因此我使用的是一个没有 begin
的简单函数体,process
和 end
块,相当于 只是 一个 end
块:
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
Write-Output "started $PSScriptRoot"
# No need to save the current value, because the modified
# value is local to the function and goes out of scope on
# exiting the function.
$ErrorActionPreference = 'SilentlyContinue';
try {
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
Write-Error $_.Exception
}
finally {
# This block is *always* called - even if Ctrl-C was used.
Create-Backup "/opt/mssql/bin/data"
# CAVEAT: If Ctrl-C was used to terminate the run,
# you can no longer produce *pipeline* input at this point
# (it will be quietly ignored).
# However, you can still output to the *host*.
Write-Host "finished!"
}
如果你真的需要支持多个输入对象,它会变得更复杂。