ออกจากแบตช์ไฟล์จากรูทีนย่อย


20

ฉันจะออกจากแบตช์ไฟล์จากภายในรูทีนย่อยได้อย่างไร

ถ้าฉันใช้คำสั่ง EXIT ฉันก็กลับไปที่บรรทัดที่ฉันเรียกรูทีนย่อยและการดำเนินการจะดำเนินต่อไป

นี่คือตัวอย่าง:

@echo off
ECHO Quitting...
CALL :QUIT
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

เอาท์พุท:

Quitting...
Still here!

ปรับปรุง:

นี่ไม่ใช่คำตอบที่เหมาะสม แต่ฉันก็ลงเอยด้วยการทำบางสิ่งตามบรรทัด:

@echo off
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL
ECHO You shouldn't see this!
GOTO END

:SUBROUTINE_WITH_ERROR
ECHO Simulating failure...
EXIT /B 1

:HANDLE_FAIL
ECHO FAILURE!
EXIT /B 1

:END
ECHO NORMAL EXIT!
EXIT /B 0

คำสั่ง double-pipe ของ:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

จดชวเลข

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

ฉันยังคงอยากรู้ว่ามีวิธีที่จะออกโดยตรงจากรูทีนย่อยแทนที่จะต้องให้ CALLER จัดการกับสถานการณ์ แต่อย่างน้อยก็ทำให้งานเสร็จ


อัปเดต # 2: เมื่อเรียกรูทีนย่อยจากภายในรูทีนย่อยอื่นที่เรียกในลักษณะข้างต้นฉันเรียกจากรูทีนย่อยภายในดังนี้:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

ด้วยวิธีนี้ข้อผิดพลาดแพร่กระจายกลับไปที่ "หลัก" เพื่อพูด ส่วนหลักของแบทช์สามารถจัดการข้อผิดพลาดได้ด้วย GOTO: FAILURE

คำตอบ:


21

เพิ่มสิ่งนี้ไปที่ด้านบนของไฟล์แบทช์ของคุณ:

@ECHO OFF
SETLOCAL

IF "%selfWrapped%"=="" (
  REM this is necessary so that we can use "exit" to terminate the batch file,
  REM and all subroutines, but not the original cmd.exe
  SET selfWrapped=true
  %ComSpec% /s /c ""%~0" %*"
  GOTO :EOF
)

จากนั้นคุณสามารถโทร:

  • EXIT [errorLevel] หากคุณต้องการออกจากไฟล์ทั้งหมด
  • EXIT /B [errorLevel] เพื่อออกจากรูทีนย่อยปัจจุบัน
  • GOTO :EOF เพื่อออกจากรูทีนย่อยปัจจุบัน

+1 สำหรับการกล่าวถึงจริงGOTO :EOF
afrazier

1
ดีมาก. ผมได้ทำการปรับเปลี่ยนเล็ก ๆ ที่กำหนด%~0ให้กับตัวแปรแทน:true if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... )ด้วยวิธีนี้คุณสามารถใช้เคล็ดลับเดียวกันในสคริปต์ชุดหลายชุดที่เรียกซึ่งกันและกัน
GolezTrol

นี่เป็นทางออกที่ดี คุณคิดว่ามันคุ้มค่าที่จะแก้ไขเพื่ออธิบายวิธีการทำงานหรือไม่ ฉันใช้เวลาสักครู่เพื่อคลายไฟล์ทั้งหมดและรู้ว่ามันเป็นจริงเพียงแค่เรียกไฟล์แบทช์ ( %~0) พร้อมกับอาร์กิวเมนต์ทั้งหมด ( %*) จาก cmd.exe ที่ซ้อนกันและ/sใช้เพื่อควบคุมวิธีที่%ComSpec%อาร์กิวเมนต์จัดการกับเครื่องหมายคำพูดคู่รอบ ๆ โทร.
ฌอน

@Sean ฉันพบว่าความกะทัดรัดมีประโยชน์มากกว่าสำหรับคนส่วนใหญ่ ไม่ได้มีการขอเอกสารเพิ่มเติมในช่วง 7 ปีที่ผ่านมาตั้งแต่ฉันเขียนมันดังนั้นจึงดูเหมือนว่าจะไม่เป็นที่ต้องการสูง ฉันยังคิดว่ามีค่าบางอย่างสำหรับคนที่ค้นหาสิ่งต่าง ๆ ด้วยตัวเองและไม่ได้ทำซ้ำเอกสาร แต่บางทีถ้ามีคนถามเพิ่มอีกซักนิด นอกจากนี้ยังเป็น CW เพื่อให้คุณสามารถเสนอการแก้ไขได้เช่นกัน
Merlyn Morgan-Graham

3

แล้วการปรับเล็กน้อยแบบนี้ล่ะ?

@echo off
ECHO Quitting...
CALL :QUIT
:: The QUIT subroutine might have set the error code so let's take a look.
IF ERRORLEVEL 1 GOTO :EOF
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

เอาท์พุท:

Quitting...

ในทางเทคนิคแล้วสิ่งนี้จะไม่ออกจากภายในรูทีนย่อย แต่จะตรวจสอบผลลัพธ์ของรูทีนย่อยและดำเนินการจากที่นั่น


2
ขอบคุณที่จะทำให้งานสำเร็จและถ้าฉันไม่สามารถหาคำตอบที่ดีกว่านั่นคือสิ่งที่ฉันจะต้องทำ อย่างไรก็ตามฉันไม่ต้องการวางบรรทัดนั้นหลังจาก CALL ทุกอันในไฟล์แบตช์ที่ยาวและซับซ้อน
สีน้ำตาล

1

หากคุณไม่ต้องการที่จะกลับมาจากขั้นตอนที่ไม่ได้ใช้call: gotoใช้แทน

@echo off
ECHO Quitting...
GOTO :QUIT
ECHO Will never be there!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

ประเด็นของคำถามคือทำอย่างไรจากรูทีนย่อย (เช่นใช้การโทร) ดังนั้นสิ่งนี้จึงไม่ตอบ
Steve Crane

1

ฉันใส่ข้อผิดพลาดในไฟล์แบตช์ คุณสามารถเรียกเครื่องมือจัดการข้อผิดพลาดเช่นนี้:

CALL :WARNING "This is" "an important" "warning."

และนี่คือจุดสิ้นสุดของไฟล์แบตช์:

::-------------------------------------------------------------------
::  Decisions
::-------------------------------------------------------------------
:INFO
IF "_DEBUG"=="true" (
  ECHO INFO: %~1
  IF NOT "%~2"=="" ECHO          %~2
  IF NOT "%~3"=="" ECHO          %~3
)
EXIT /B 0
:WARNING
ECHO WARNING: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
EXIT /B 0
:FAILURE
ECHO FAILURE: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
pause>nul
:END
ECHO Closing Server.bat script
FOR /l %%a in (5,-1,1) do (TITLE %TITLETEXT% -- closing in %%as&PING.exe -n 2 -w 1 127.0.0.1>nul)

1

สิ่งนี้จะออกจากบริบทปัจจุบันและบริบทหลัก (เช่นเมื่อดำเนินการภายในcallสคริปต์รูทีนย่อยลึกหนึ่งสคริปต์จะออก):

(goto) 2>nul || exit /b

หรือหากคุณต้องการ errorlevel 0:

(goto) 2>nul || (
    type nul>nul
    exit /b
)

โดยทั่วไป(goto) 2>nulตั้งค่าระดับข้อผิดพลาดเป็น 1 (โดยไม่แสดงข้อผิดพลาด) ส่งคืนการดำเนินการไปยังบริบทหลักและรหัสหลังจากดำเนินการไพพ์สองครั้งในบริบทหลัก type nul>nulตั้ง errorlevel เป็น 0

UPD:

หากต้องการส่งคืนการดำเนินการมากกว่าสองครั้งติดต่อกันให้โยงหลาย ๆ(goto) 2>nul ||อย่างไว้ดังนี้:

(goto) 2>nul || (goto) 2>nul || (goto) 2>nul || (
    type nul>nul
    exit /b
)

นี่คือรูทีนย่อยแบบเรียกซ้ำเพื่อกลับบริบทจำนวนครั้งของตัวแปร:

:Kill
(goto) 2>nul || (
    set /a depth=%1-1
    if %1 GEQ 1 (
        call:Kill !depth!
    )
    (goto) 2>nul || (type nul>nul)
)

เมื่อเรียกจากฟังก์ชั่นวนซ้ำ:

@echo off
setlocal EnableDelayedExpansion
call:Recurs 5
echo This won't be printed
exit /b

:Recurs
set /a ri+=1
echo %ri%
if %ri% LSS %1 (
    call:Recurs %1
)
echo This will be printed only once
call:Kill %1
exit /b

ผลลัพธ์จะเป็น:

1
2
3
4
5
This will be printed only once
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.