Thoát khỏi tệp bó từ chương trình con


20

Làm cách nào tôi có thể thoát một tệp bó từ bên trong chương trình con?

Nếu tôi sử dụng lệnh EXIT, tôi chỉ cần quay lại dòng mà tôi đã gọi chương trình con và tiếp tục thực hiện.

Đây là một ví dụ:

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Đầu ra:

Quitting...
Still here!

Cập nhật:

Đây không phải là một câu trả lời thích hợp, nhưng cuối cùng tôi đã làm một cái gì đó theo dòng:

@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

Tuyên bố hai ống của:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

là viết tắt của:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

Tôi vẫn muốn biết liệu có cách nào để thoát trực tiếp khỏi chương trình con hơn là phải làm cho CALLER xử lý tình huống hay không, nhưng điều này ít nhất sẽ hoàn thành công việc.


Cập nhật # 2: Khi gọi một chương trình con từ bên trong một chương trình con khác, được gọi theo cách trên, tôi gọi từ trong chương trình con như vậy:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

Bằng cách này, lỗi lan truyền trở lại "chính", có thể nói như vậy. Phần chính của lô sau đó có thể xử lý lỗi với trình xử lý lỗi GOTO: FAILURE

Câu trả lời:


21

Thêm phần này vào đầu tệp bó của bạn:

@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
)

Sau đó, bạn có thể chỉ cần gọi:

  • EXIT [errorLevel] nếu bạn muốn thoát toàn bộ tập tin
  • EXIT /B [errorLevel] để thoát khỏi chương trình con hiện tại
  • GOTO :EOF để thoát khỏi chương trình con hiện tại

+1 để thực sự đề cậpGOTO :EOF
afrazier

1
Rất đẹp. Tôi đã thực hiện một sửa đổi nhỏ, gán %~0cho biến thay vì true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). Bằng cách đó, bạn có thể sử dụng cùng một mẹo trong nhiều tập lệnh gọi nhau.
GolezTrol

Đây là một giải pháp tuyệt vời. Bạn có nghĩ rằng nó đáng để chỉnh sửa để giải thích cách nó hoạt động? Tôi mất một phút để giải nén tất cả những điều đó và nhận ra rằng nó thực sự chỉ đang gọi tệp bó ( %~0) với tất cả các đối số ( %*) từ một cmd.exe lồng nhau và /sđược sử dụng để kiểm soát cách %ComSpec%đối số xử lý các dấu ngoặc kép xung quanh gọi.
Sean

@Sean Tôi thấy ngắn gọn là hữu ích hơn cho hầu hết mọi người. Nhiều tài liệu đã không được yêu cầu trong 7 năm kể từ khi tôi viết nó, vì vậy nó dường như không có nhu cầu cao. Tôi cũng nghĩ rằng có một số giá trị cho mọi người tự tìm kiếm mọi thứ và không bị lừa / phân đoạn tài liệu. Nhưng có lẽ nếu một vài người hỏi tôi có thể thêm một cái gì đó. Đây cũng là một CW để bạn cũng có thể đề xuất chỉnh sửa
Merlyn Morgan-Graham

3

Làm thế nào về điều chỉnh nhỏ này?

@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

Đầu ra:

Quitting...

Về mặt kỹ thuật, điều này không thoát ra khỏi chương trình con. Thay vào đó, nó chỉ đơn giản là kiểm tra kết quả của chương trình con và thực hiện hành động từ đó.


2
Cảm ơn, điều đó chắc chắn sẽ hoàn thành công việc, và nếu tôi không thể tìm thấy câu trả lời tốt hơn đó là điều tôi sẽ phải làm. Tuy nhiên, tôi không cần phải dán dòng đó sau mỗi lần GỌI trong tệp bó dài và phức tạp của mình.
Brown

1

Nếu bạn không muốn quay lại từ quy trình, đừng sử dụng call: thay vào đó hãy sử dụng goto.

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Điểm của câu hỏi là làm thế nào để thực hiện nó từ chương trình con (tức là sử dụng cuộc gọi) để điều này không trả lời nó.
Steve Crane

1

Tôi đặt xử lý lỗi trong các tập tin hàng loạt của tôi. Bạn có thể gọi các trình xử lý lỗi như thế này:

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

Và đây là phần cuối của tệp bó:

::-------------------------------------------------------------------
::  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

Điều này sẽ thoát khỏi bối cảnh hiện tại và bối cảnh cha mẹ (nghĩa là, khi được thực thi bên trong một callkịch bản chương trình con sâu sẽ thoát):

(goto) 2>nul || exit /b

Hoặc, nếu bạn cần errorlevel 0:

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

Về cơ bản, (goto) 2>nulđặt errorlevel thành 1 (không xuất ra lỗi), trả về thực thi cho bối cảnh cha và mã sau khi ống đôi được thực thi trong ngữ cảnh cha. type nul>nulđặt errorlevel thành 0.

CẬP NHẬT:

Để trả về thực thi nhiều hơn hai lần liên tiếp, chuỗi một số (goto) 2>nul ||như thế này:

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

Đây là một chương trình con đệ quy để trả về bối cảnh một số lần thay đổi:

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

Khi được gọi từ một hàm đệ quy:

@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

đầu ra sẽ là:

1
2
3
4
5
This will be printed only once
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.