【问题标题】:Stop execution of PowerShell script and return to original batch file停止执行 PowerShell 脚本并返回原始批处理文件
【发布时间】:2016-09-08 10:07:58
【问题描述】:

如果此 PowerShell 脚本调用的批处理脚本引发错误,我将无法停止 PowerShell 脚本。

蝙蝠 1

PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%\CallInstall.ps1'";

CallInstall.ps1 调用少量批处理文件和 vbs 文件,如果先决条件失败,bat2 会调用基于 vbs 的消息框以显示错误。理想情况下,此时执行应该停止,bat1 应该开始剩余的进程,但在我的情况下,CallInstall.ps1 在返回 bat1 之前仍然执行所有批处理和 vbs 脚本。

CallInstall.ps1:

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null

Set-Location $PSScriptRoot

$ScriptsHome = Get-Item '.\ScriptInstall\*'

#Define Form
$Form = New-Object System.Windows.Forms.Form
$Form.width = 1000
$Form.height = 200
$Form.Text = "** Installation in Progress**"
$Form.Font = New-Object System.Drawing.Font("Times New Roman" ,12, [System.Drawing.FontStyle]::Regular)
$Form.MinimizeBox = $False
$Form.MaximizeBox = $False
$Form.WindowState = "Normal"
$Form.StartPosition = "CenterScreen"
$Form.Opacity = .8
$Form.BackColor = "Gray"

# Init ProgressBar
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Maximum = $ScriptsHome.Count
$ProgressBar.Minimum = 0
$ProgressBar.Location = new-object System.Drawing.Size(10,70)
$ProgressBar.size = new-object System.Drawing.Size(967,10)
$Form.Controls.Add($ProgressBar)
#$Form.Controls.Add($MessagesLabel)

#Running Script Name
#$Label = New-Object System.Windows.Forms.Label
#$Label.AutoSize = $true
#$Label.Location = New-Object System.Drawing.Point(10,50) 
#$Form.Controls.Add($Label)

#Define Array messages
#Array
$Messages = @("Message 1",
              "Message 2",
              "Message 3",
              "Message 4",
              "Message 5",
            )
$MessagesLabel = New-Object System.Windows.Forms.Label
$MessagesLabel.AutoSize = $true
$MessagesLabel.Location = New-Object System.Drawing.Point(10,50)
$Form.Controls.Add($MessagesLabel)

# Add_Shown action    
$ShownFormAction = {
    $Form.Activate()

    foreach ($script in $ScriptsHome) {
        $ProgressBar.Increment(1)
        $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
        Start-Process $script.FullName -Wait -WindowStyle Hidden
    }
    $Form.Dispose()
}
$Form.Add_Shown($ShownFormAction)

# Show Form
$Form.ShowDialog()

#Create Logs
Invoke-Expression -Command .\LogScript.ps1

在 ScriptInstall 文件夹中,其中一个批处理脚本上有 10 个批处理和 vbs 脚本:

:error
cscript %base_dir%\fail.vbs > %Log%
:match
findstr /I /c:"xxxx" c:\match.txt
if %errorlevel% == 0 (
    goto nextStep
) else (
    goto ErrorCheck
)

fail.vbs:

Dim vbCrLf : vbCrLf = Chr(13) & Chr(10) 
Set WshShell = WScript.CreateObject("WScript.Shell")
MsgBox "Installation Fail !!!" & vbCrLf & "Message1" & vbCrLf & "Click OK to Exit" , 16, "Fail"

理想情况下,在此fail.vbs 框上按“确定”CallInstall.ps1 不应继续执行 ScriptInstall 文件夹中的其余脚本,但它会执行其他操作。它继续 ScriptInstall 文件夹中的剩余脚本,然后将执行返回到 bat1。

【问题讨论】:

  • 如果可能的话,维护逻辑和停止/继续步骤会容易得多,如果您将尽可能多的逻辑移动到一种脚本类型中,而不是从 bat 调用 PowerShell,然后调用其他批处理文件调用 vbscript。在您的实例“原样”中,没有确定/取消,您需要从您的 vbscript 返回一个非零错误代码,如下所示:WScript.Quit 1

标签: powershell batch-file vbscript


【解决方案1】:

您可以使用退出代码并像这样更改您的脚本:

PowerShell 脚本:

foreach ($script in $ScriptsHome) {
    $ProgressBar.Increment(1)
    $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
    $process = Start-Process $script.FullName -Wait -WindowStyle Hidden -passthru
    if($process.ExitCode -ne 0){break}
}

批处理脚本:

:error
cscript %base_dir%\fail.vbs > %Log%
Exit /b 1

【讨论】:

  • 我在第一个批处理文件中也尝试使用echo yes > c:\batchpass.txt 进行此修改,但调用第一个脚本后powershell 退出。
  • 我不确定你的问题是什么。但我假设powershell退出是因为你的批处理文件没有以0退出。你能发布那个批处理文件的完整代码吗?如果您只执行批处理文件会发生什么。这是它应该做的吗?
  • 批处理文件有net stop "Acronis VSS Provider" net stop "COM+ System Application"
  • 如果您自己在命令行上运行批处理脚本,而不是由 powershell 脚本触发。那会发生什么?它的 %errorlevel% 是什么?如果 %errorlevel% 不为 0,则问题出在您的批处理脚本中,而不是修改后的 powershell 脚本。
  • 是的,错误级别不是 0,但问题是我创建这些脚本时要记住,某些服务器可能有一些服务,而有些服务器可能没有这些相同的服务,无论哪种方式我都必须包括所有使用最终安装脚本之前可能的 Windows 服务。
【解决方案2】:

Exit 将从 Powershell 退出。因此,如果您以交互方式运行,这是出乎意料的。改用Return,停止执行剩余的代码行:

For ($i=0; $i -lt 5; $i++){
    Write-Host
    $input = $(Read-Host "Enter a number between 1 and 5")

    If ($input -In (1..5)){
        $MyIntNum = $input -as [int]
        Break
    }ElseIf($input -eq '0'){
        Return 
    }
    Else{
        Write-Host 'Please try again ...'
    }
}

Write-Host
Write-Host "You entered $input"

输入0以外的任何数字时输出:

Enter a number between 1 and 5: 9
Please try again ...

Enter a number between 1 and 5: 7
Please try again ...

Enter a number between 1 and 5: 4

You entered 4

输入0时的结果:

Enter a number between 1 and 5: 0

【讨论】:

    【解决方案3】:

    一般的方法是在出现错误时使用非零退出代码退出批处理脚本,并检查 PowerShell 代码中的退出状态,正如@A189198 已经在他的回答中显示的那样。

    但是,我会完全删除 VBScript(您也可以从 PowerShell 显示消息框),并且也不需要捕获 Process 对象,因为 PowerShell 会自动通过 automatic variable $LastExitCode 提供信息。

    将批处理代码中的错误处理更改为如下内容:

    if %errorlevel% neq 0 exit /b 1
    

    并更改此 foreach 循环:

    foreach ($script in $ScriptsHome) {
        $ProgressBar.Increment(1)
        $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
        Start-Process $script.FullName -Wait -WindowStyle Hidden
    }
    $Form.Dispose()
    

    到这里:

    foreach ($script in $ScriptsHome) {
        $ProgressBar.Increment(1)
        $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
        Start-Process $script.FullName -Wait -WindowStyle Hidden
        if ($LastExitCode -ne 0) {
            [Windows.Forms.MessageBox]::Show("Installation Fail!`nMessage1`nClick OK to Exit", 'Fail', 0, 'Error')
            break
        }
    }
    $Form.Dispose()

    【讨论】:

    • 对于延迟的回复,我深表歉意。我尝试了这种修改,但只执行了第一个脚本,然后在调用消息框后 powershell 退出。第一个批处理脚本包含echo yes > c:\batchpass.txt 我不明白我做错了什么。
    • 显然您的第一个批处理脚本已经返回一个非零退出代码,因此 PowerShell 脚本将按照您的要求终止。如果批处理脚本只包含 echo 语句,则这可能是由于写入输出文件时出错引起的。否则你需要显示你的批处理文件的代码。
    • 是的,你是绝对正确的。在我的第一个批处理文件中,我使用 net stop 命令来停止各种服务。其中一些服务是我试图停止的通用 Windows 服务,但最终有时会发生某些已经停止的服务在执行脚本时会出错。任何处理这个问题的方法。
    • 请为此发布一个新问题。
    • 我不是在询问如何处理如果我试图停止的服务已经停止时发生的错误。我想说的是,这可能是一个问题,因为 $LastExitCode 终止了 powershell 脚本。牢记这一点,如何以及在 poweshell 脚本中修改什么?
    【解决方案4】:

    感谢每个人向我展示如何做到这一点。我已经找到了解决方案。 我已经从

    更改了 powershell 脚本中的 foreach 循环
    foreach ($script in $ScriptsHome) {
        $ProgressBar.Increment(1)
        $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
        Start-Process $script.FullName -Wait -WindowStyle Hidden
    }
    $Form.Dispose()
    

    foreach ($script in $ScriptsHome) {
            $ProgressBar.Increment(1)
            $MessagesLabel.Text = $Messages[$ProgressBar.Value - 1]
            #$Label.Text     = "$($script.Name)"   
            Start-Process $script.FullName -Wait -WindowStyle Hidden
    
            if (Select-String -Path C:\exitloop.txt "fail") 
            {
             break 
            } else {continue
            }
            }
        $Form.Dispose()
    

    在 ScriptInstall 文件夹中,批处理脚本中有 10 个批处理和 vbs 脚本,我已将代码更改为。

    :error
    cscript %base_dir%\fail.vbs > %Log%
    echo fail > c:\exitloop.txt
    exit
    :match
    findstr /I /c:"xxxx" c:\match.txt
    if %errorlevel% == 0 (
        goto nextStep
    ) else (
        goto ErrorCheck
    )
    

    因此,每当批处理文件中出现条件失败时,批处理文件将生成一个带有文本“失败”的 txt 文件,这会在该特定批处理文件终止之前发生。之后,控件将立即返回到 powershell 脚本,该脚本将检查 c:\exitloop.txt 中是否存在“失败”,如果是,这将停止 powershell 脚本本身的执行,否则它将继续调用批处理和 vbs 文件。 谢谢

    【讨论】:

      猜你喜欢
      • 2023-03-30
      • 2011-09-05
      • 1970-01-01
      • 2023-03-30
      • 2012-10-20
      • 2013-10-10
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      相关资源
      最近更新 更多