Reputation: 226
I'm trying to call one bat file from another, while keeping the target variables local to itself(mostly). The code below is my failed attempt, the var i want is missing, but the local is still around. I've found some info on using setlocal here, which is how I think I have it. Also the info I'm using to push a var past setlocal is here. It's very possible I'm missing something from these. Any help is appreciated!
The calling bat:
@SET errmsg=Old Message
@CALL Target.bat TEST_JOB errmsg
@ECHO.jobname = %jobname%, but should be undefined
@IF DEFINED errmsg ECHO.Error occurred: %ERRORLEVEL% %errmsg%
The target bat:
@SETLOCAL
@ECHO OFF
:START
SET jobname=%~1
SET errmsgvar=%~2
:MAIN
CALL:ERROR "New Error Message"
ECHO.Should have never returned here
ENDLOCAL
:EXIT
@ECHO ON
@EXIT /b %ERRORLEVEL%
:ERROR "errorMesage"
ENDLOCAL & SET "%errmsgvar%=%~1"
ECHO.%errmsg% ^<-- Testing only, we don't know what the actual var will be
(GOTO) 2>NUL & GOTO:EXIT
The result:
C:\Projects\Batch>Caller.bat
New Error Message <-- Testing only, we don't know what the actual var will be
jobname = TEST_JOB, but should be undefined
Error occurred: 0 Old Message
Edit for clarity:
I'm trying to accomplish several things here...
It seems like my endlocal is being ignored, and its just doing endlocal at the end of the file, which breaks things
Upvotes: 2
Views: 669
Reputation: 130829
As Magoo said, your TEST_JOB value is cleared properly. Your test must have been looking at a holdover result from a prior run. Best to explicitly clear the value in your calling bat, prior to the CALL, so you can never get a false result..
Your errmsg is not being set because of a flaw in your logic. More on that after I provide a little introduction to (goto) 2>nul
.
You are attempting to use a relatively new (goto) 2>nul
technique that is not widely known. It is effectively an exit /b
except additional concatenated commands still execute, but in the context of the caller. I believe the first discovery was published at http://forum.script-coding.com/viewtopic.php?id=9050 (which I cannot read), and the first known English posting is at http://www.dostips.com/forum/viewtopic.php?f=3&t=6491.
Since that DosTips post, many useful tools have resulted:
Within your code, you try to ENDLOCAL in your error routine, but that only affects SETLOCAL that were issued while in your routine. You need to use the (goto) 2>nul
trick prior to issuing the ENDLOCAL so that ENDLOCAL works properly on the parent context.
:error "errormesage"
(
(goto) 2>nul
endlocal
set "%errmsgvar%=%~1"
goto :exit
)
I'm worried about your returned ERRORLEVEL. I don't think it is returning the value you want when there is an error.
You must remember that all external commands always set the ERRORLEVEL whether there was an error or not. Almost all internal commands set the ERRORLEVEL to non-zero upon error, and some internal commands set the ERRORLEVEL to 0 upon success (but some preserve the existing ERRORLEVEL if there was no error).
It is safest to explicitly pass your desired error number into your :error routine, and then explicitly set the value upon exit. Here is one way to do it:
:error "errMsg" errNum
(
(goto) 2>nul
endlocal
set "%errmsgvar%=%~1"
cmd /c exit %2
goto :exit
)
But I would modify things so that you always exit the same way regardless whether there was an error or not. The :exit routine below is called with the error message and error number if there was an error, and the values are set appropriately. Upon normal exit, neither value is passed in, so the errmsg variable gets cleared, and the routine returns with the current ERRORLEVEL (presumably 0).
Calling script
@setlocal
@set errmsg=Old Message
@set "jobname="
@call target.bat TEST_JOB errmsg
@if defined jobname (
echo ERROR - jobname = %jobname%
) else (
echo SUCCESS - jobname is undefined
)
@if defined errmsg (
echo.Error occurred: %errorlevel% %errmsg%
) else (
echo No error occurred
)
Target.bat
@echo off
setlocal enableDelayedExpansion
:start
set jobname=%~1
set errmsgvar=%~2
:main
set /a "1/(%random% %% 3)" || call :exit !errorlevel! "Random error"
echo Success
call :exit
:exit [errNum] ["errMsg"]
(
(goto) 2>nul
endlocal
set "%errmsgvar%=%~2"
echo on
exit /b %1
)
The :main code randomly raises an error 33% of the time when it attempts to divide by zero.
Here is some sample output, showing both possible outcomes:
C:\test>test
Success
SUCCESS - jobname is undefined
No error occurred
C:\test>test
Divide by zero error.
SUCCESS - jobname is undefined
Error occurred: 1073750993 Random error
Upvotes: 3
Reputation: 80023
Worked perfectly for me.
I believe your test set jobname
at some stage and hence your setlocal/endlocal
frames simply restore the original environment.
I'd suggest you execute
set "jobname="
as the first line of your calling bat to force jobname
to be clear before the action starts.
Upvotes: 1
Reputation: 4750
Change 2nd last line to
ECHO.%errmsgvar%.....
to match the 3rd to last line
By way of explanation... ENDLOCAL & SET "errmsgvar=%~1" 1. The entire line loaded and %~1 is expanded. 2. ENDLOCAL is executed and ends localization. 3. SET "errmsgvar=%~1" is executed as... SET "errmsgvar=WhateverWasIn%~1" This technique is tunneling and is a way to pass variables across locales.
Upvotes: 0
Reputation: 249
Change your second script to the following. If accounts for the ENDLOCAL
at the end of the script. I believe the ENDLOCAL
in the :ERROR
block is for that block only and not the entire file.
@SETLOCAL
@ECHO OFF
:START
SET jobname=%~1
SET errmsgvar=%~2
:MAIN
CALL:ERROR "New Error Message"
ECHO.Should have never returned here
ENDLOCAL
:EXIT
REM case insensitive test for errmsgvar.
IF DEFINED errmsgvar (
IF /I NOT "%errmsgvar%" == "" (
ENDLOCAL & SET "errmsg=%errmsg%"
)
)
@ECHO ON
@EXIT /b %ERRORLEVEL%
:ERROR "errorMesage"
ENDLOCAL & SET "errmsgvar=%~1"
SET "errmsg=%errmsgvar%"
ECHO.%errmsg% ^<-- Testing only, we don't know what the actual var will be
(GOTO) 2>NUL & GOTO:EXIT
Output becomes (extra line breaks for clarity):
Caller.bat
New Error Message <-- Testing only, we don't know what the actual var will be
jobname = TEST_JOB
Error occurred: 0 New Error Message
Upvotes: 0