ฉันต้องการให้สคริปต์ PowerShell ของฉันหยุดทำงานเมื่อคำสั่งใด ๆ ที่ฉันเรียกใช้ล้มเหลว (เช่นset -e
ในทุบตี) ฉันใช้ทั้งคำสั่ง Powershell ( New-Object System.Net.WebClient
) และโปรแกรม ( .\setup.exe
)
ฉันต้องการให้สคริปต์ PowerShell ของฉันหยุดทำงานเมื่อคำสั่งใด ๆ ที่ฉันเรียกใช้ล้มเหลว (เช่นset -e
ในทุบตี) ฉันใช้ทั้งคำสั่ง Powershell ( New-Object System.Net.WebClient
) และโปรแกรม ( .\setup.exe
)
คำตอบ:
$ErrorActionPreference = "Stop"
คุณจะได้เป็นส่วนหนึ่งของวิธีการที่นั่น (เช่นนี้ทำงานได้ดีสำหรับ cmdlets)
อย่างไรก็ตามสำหรับ EXE คุณจะต้องตรวจสอบ$LastExitCode
ตัวเองทุกครั้งหลังจากการเรียกใช้ exe และตรวจสอบว่าล้มเหลวหรือไม่ น่าเสียดายที่ฉันไม่คิดว่า PowerShell สามารถช่วยได้ที่นี่เพราะใน Windows EXE ไม่สอดคล้องกันอย่างมากกับสิ่งที่ถือเป็นรหัสออก "สำเร็จ" หรือ "ล้มเหลว" ส่วนใหญ่ปฏิบัติตามมาตรฐาน UNIX ที่ระบุถึงความสำเร็จ แต่ไม่ใช่ทั้งหมดที่ทำได้ ลองใช้ฟังก์ชั่น CheckLastExitCode ในบล็อกโพสต์นี้ คุณอาจพบว่ามันมีประโยชน์.
throw "$exe failed with exit code $LastExitCode"
ที่ $ exe EXE
คุณควรจะสามารถทำได้โดยใช้คำสั่ง$ErrorActionPreference = "Stop"
ที่จุดเริ่มต้นของสคริปต์ของคุณ
การตั้งค่าเริ่มต้น$ErrorActionPreference
คือContinue
ซึ่งเป็นสาเหตุที่คุณเห็นสคริปต์ของคุณดำเนินต่อไปหลังจากเกิดข้อผิดพลาด
น่าเศร้าเนื่องจาก cmdlet buggy เช่น New-RegKey และ Clear-Diskจึงไม่มีคำตอบเหล่านี้เพียงพอ ขณะนี้ฉันได้ตัดสินในบรรทัดต่อไปนี้ที่ด้านบนสุดของสคริปต์ PowerShell ใด ๆ เพื่อรักษาสติของฉัน
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
และจากนั้นการโทรในประเทศก็ได้รับการรักษานี้:
native_call.exe
$native_call_success = $?
if (-not $native_call_success)
{
throw 'error making native call'
}
รูปแบบการโทรแบบดั้งเดิมนั้นค่อย ๆ กลายเป็นเรื่องธรรมดาพอสำหรับฉันที่ฉันอาจจะต้องดูตัวเลือกต่าง ๆ เพื่อทำให้รัดกุมยิ่งขึ้น ฉันยังคงเป็นมือใหม่ที่มีพลังดังนั้นข้อเสนอแนะยินดีต้อนรับ
คุณต้องการการจัดการข้อผิดพลาดที่แตกต่างกันเล็กน้อยสำหรับฟังก์ชั่น powershell และการเรียก exe และคุณต้องแน่ใจว่าได้แจ้งผู้โทรของสคริปต์ของคุณว่าล้มเหลว การสร้างที่อยู่ด้านบนของExec
จากไลบรารี Psake สคริปต์ที่มีโครงสร้างด้านล่างจะหยุดข้อผิดพลาดทั้งหมดและสามารถใช้เป็นเทมเพลตฐานสำหรับสคริปต์ส่วนใหญ่ได้
Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"
# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
to see if an error occcured. If an error is detected then an exception is thrown.
This function allows you to run command-line programs without having to
explicitly check the $lastexitcode variable.
.EXAMPLE
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
Try {
# Put all your stuff inside here!
# powershell functions called as normal and try..catch reports errors
New-Object System.Net.WebClient
# call exe's and check their exit code using Exec
Exec { setup.exe }
} Catch {
# tell the caller it has all gone wrong
$host.SetShouldExit(-1)
throw
}
Exec { sqlite3.exe -bail some.db "$SQL" }
นี้-bail
ทำให้เกิดข้อผิดพลาดเพราะมันพยายามที่จะตีความมันเป็นพารามิเตอร์ Cmdlet? การห่อสิ่งต่าง ๆ ด้วยคำพูดดูเหมือนจะไม่ทำงาน ความคิดใด ๆ
การแก้ไขคำตอบเล็กน้อยจาก @alastairtree:
function Invoke-Call {
param (
[scriptblock]$ScriptBlock,
[string]$ErrorAction = $ErrorActionPreference
)
& @ScriptBlock
if (($lastexitcode -ne 0) -and $ErrorAction -eq "Stop") {
exit $lastexitcode
}
}
Invoke-Call -ScriptBlock { dotnet build . } -ErrorAction Stop
ความแตกต่างที่สำคัญคือ:
Invoke-Command
)-ErrorAction
พฤติกรรมเลียนแบบจากการสร้างใน cmdletsInvoke-Call { dotnet build $something }
& @ScriptBlock
และ& $ScriptBlock
ดูเหมือนจะทำสิ่งเดียวกัน ยังไม่สามารถ google สิ่งที่แตกต่างในกรณีนี้
ฉันใหม่กับ powershell แต่ดูเหมือนว่าจะมีประสิทธิภาพมากที่สุด:
doSomething -arg myArg
if (-not $?) {throw "Failed to doSomething"}
ฉันมาที่นี่เพื่อค้นหาสิ่งเดียวกัน $ ErrorActionPreference = "หยุด" ฆ่าเปลือกของฉันทันทีเมื่อฉันอยากเห็นข้อความแสดงข้อผิดพลาด (หยุดชั่วคราว) ก่อนที่มันจะจบลง ย้อนกลับไปที่ความอ่อนไหวแบบกลุ่มของฉัน:
IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF
ฉันพบว่ามันใช้งานได้ดีเหมือนกันกับสคริปต์ ps1 ของฉัน:
Import-PSSession $Session
If ($? -ne "True") {Pause; Exit}
เปลี่ยนเส้นทางstderr
ไปยังstdout
ดูเหมือนว่าจะยังทำเคล็ดลับโดยไม่ต้องคำสั่งอื่น ๆ / ห่อ scriptblock ใด ๆ แม้ว่าฉันไม่สามารถหาคำอธิบายว่าทำไมมันทำงานแบบว่า ..
# test.ps1
$ErrorActionPreference = "Stop"
aws s3 ls s3://xxx
echo "==> pass"
aws s3 ls s3://xxx 2>&1
echo "shouldn't be here"
สิ่งนี้จะเอาท์พุตต่อไปนี้ตามที่คาดไว้ (คำสั่งaws s3 ...
คืน$LASTEXITCODE = 255
)
PS> .\test.ps1
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
==> pass
$ErrorActionPreference = "Stop"
งานได้กับโปรแกรมที่ทำงานได้ดี (ที่ส่งคืน 0 เมื่อสำเร็จ)