Page 1 of 1
Tricky way to detect calls from another batch script
Posted: 07 Jun 2015 09:23
by siberia-man
http://forum.script-coding.com/viewtopic.php?id=9050There is fruitful discussion (in Russian) that has began as the report about bug with "goto nonexistent_label" and finished with nice way of detecting the caller script
Code: Select all
@echo off
:: http://forum.script-coding.com/viewtopic.php?pid=94390#p94390
if not defined CMDCALLER (
(echo on & goto) 2>nul
call set "CMDCALLER=%%~f0"
call "%~f0" %*
set "CMDCALLER="
)
if /i "%CMDCALLER%" == "%%~f0" set "CMDCALLER="
:: Useful code begins here
echo:caller : %CMDCALLER%
echo:arguments : %*
Re: Tricky way to detect calls from another batch script
Posted: 07 Jun 2015 10:34
by dbenham

Very cool and elegant
Re: Tricky way to detect calls from another batch script
Posted: 09 Jun 2015 08:21
by Aacini
This is very funny (and strange)! Each execution of "(goto)" (the "echo on &" part is not necessary),
cancel the execution of the last called subroutine!

Check this:
FirstOne.batCode: Select all
@echo off
setlocal
set var=First One
echo FirstOne.bat, calling Second.bat:
call Second.bat
echo Returned to FirstOne.bat, var = %var%
Second.batCode: Select all
@echo off
setlocal
set var=Second
echo Second.bat, calling TheThird.bat:
call TheThird.bat
echo Returned to Second.bat, var = %var%
TheThird.batCode: Select all
@echo off
setlocal
set var=The Third
echo TheThird.bat, calling XYZ.bat:
call XYZ.bat
echo Returned to TheThird.bat, var = %var%
XYZ.batCode: Select all
@echo off
setlocal
set var=XYZ
echo XYZ.bat, calling CancelPreviousCalls.bat:
call CancelPreviousCalls.bat
echo Returned to XYZ.bat, var = %var%
CancelPreviousCalls.batCode: Select all
@echo off
setlocal
set var=Cancel Previous Calls
echo ---------------------------
echo I am CancelPreviousCalls.bat
for /L %%i in (1,1,%CANCEL%) do (
call echo Cancel %%i- "%%~NX0", var = %%var%%
(goto) 2> NUL
)
echo Ending CancelPreviousCalls.bat
echo -----------------------------
This is the output of a test:
Code: Select all
C:\ set CANCEL=0
C:\ FirstOne.bat
FirstOne.bat, calling Second.bat:
Second.bat, calling TheThird.bat:
TheThird.bat, calling XYZ.bat:
XYZ.bat, calling CancelPreviousCalls.bat:
---------------------------
I am CancelPreviousCalls.bat
Ending CancelPreviousCalls.bat
-----------------------------
Returned to XYZ.bat, var = XYZ
Returned to TheThird.bat, var = The Third
Returned to Second.bat, var = Second
Returned to FirstOne.bat, var = First One
C:\ set CANCEL=2
C:\ FirstOne.bat
FirstOne.bat, calling Second.bat:
Second.bat, calling TheThird.bat:
TheThird.bat, calling XYZ.bat:
XYZ.bat, calling CancelPreviousCalls.bat:
---------------------------
I am CancelPreviousCalls.bat
Cancel 1- "CancelPreviousCalls.bat", var = Cancel Previous Calls
Cancel 2- "XYZ.bat", var = XYZ
Returned to TheThird.bat, var = The Third
Returned to Second.bat, var = Second
Returned to FirstOne.bat, var = First One
C:\ set CANCEL=4
C:\ FirstOne.bat
FirstOne.bat, calling Second.bat:
Second.bat, calling TheThird.bat:
TheThird.bat, calling XYZ.bat:
XYZ.bat, calling CancelPreviousCalls.bat:
---------------------------
I am CancelPreviousCalls.bat
Cancel 1- "CancelPreviousCalls.bat", var = Cancel Previous Calls
Cancel 2- "XYZ.bat", var = XYZ
Cancel 3- "TheThird.bat", var = The Third
Cancel 4- "Second.bat", var = Second
Returned to FirstOne.bat, var = First One
Antonio
Re: Tricky way to detect calls from another batch script
Posted: 09 Jun 2015 09:47
by jeb
The GOTO works here like an EXIT /b or GOTO :EOF, with the difference that the loop doesn't break.
And the batch file context is still enabled until the first called batch is leaved this way.
Code: Select all
for /L %%i in (1,1,%CANCEL%) do (
call set "isCmdLineContext=%%"
if defined isCmdLineContext echo We are in CmdLineContext
call echo Cancel %%i- "%%~NX0", var = %%var%%
(goto) 2> NUL
)
The advantage is that even ENDLOCAL will be executed for every batch file and function.
So this can be used as the first step for a call/stack dump.
Combined with the GetLineNumber technic, it can be used also to get the (more or less) correct line numbers, where the return ends up.
Btw.
I only develop the GetLineNumber technic to create a call dump

Currently the GetLineNumber doesn't need this trick for a single file,
but with the GOTO trick this can even work over multiple files.

Re: Tricky way to detect calls from another batch script
Posted: 10 Jun 2015 15:32
by jeb
I played today a bit with the new (goto) technic and I discovered two important points
Reminder:
A (goto) exits and removes one CALL-stack entry and also implicit closes all open SETLOCAL of the entry.
But cached code (parenthesis blocks or ampersand) are still cached and will be executed later.
1) A simple EXIT /B or even a goto :existantLabel WILL remove cached code
This works even if multiple (goto) exits multiple call-stack entries, then a single EXIT /B removes all cached blocks,
but only of the just exited entries, all others are still valid.
2) (goto) will not just exits a call-stack entry it will also move the file position at the position after the CALLER command or block
These two points open many possible new technics

One I have prepared today, I will show at
Exception Handling with batch using erroneous GOTOPoint 2 was not obvious to me but it can be simply demonstrated.
Code: Select all
@echo off
cls
setlocal
echo START Main
call :func+1
echo END Main
exit /b
:next
echo NEXT 1 ********************* NEXT 1
exit /b
:func
setlocal EnableDelayedExpansion
set "fn=%0"
set "fn=%fn:*+=%"
set /a cnt=fn + 1
echo START %0
(
(
if %cnt% GEQ 10 (
call :cancel
) ELSE (
call :func+%cnt%
)
) && echo +++ %0 OK || ( echo FAIL %0 *************** & (call ))
(call )
echo INSIDE BLOCK %0 fn=!fn!
)
echo END %0
echo(
exit /b
:next
echo NEXT 2 ********************* NEXT 2
exit /b
:cancel
echo(
echo *************** CANCEL STARTS %count%
set CANCEL=4
for /L %%a in (1 1 %CANCEL%) DO (
(goto) 2>NUL
call set "isCmdLineContext=%%"
if defined isCmdLineContext echo isCmdLineContext
call echo - %%0 %%1 fn=%%fn%% %%=undefined%%
if %%a == %CANCEL% (
echo *************** CANCEL ENDS
echo(
goto :next
)
)
echo *************** NEVER COMES HERE - CANCEL ENDS
exit /b
:next
echo NEXT END ********************* NEXT END
exit /b
Output wrote:START Main
START :func+1
START :func+2
START :func+3
START :func+4
START :func+5
START :func+6
START :func+7
START :func+8
START :func+9
*************** CANCEL STARTS
- :func+9 fn=9
- :func+8 fn=8
- :func+7 fn=7
- :func+6 fn=6
*************** CANCEL ENDS
NEXT 2 ********************* NEXT 2
+++ :func+5 OK
INSIDE BLOCK :func+5 fn=5
END :func+5
+++ :func+4 OK
INSIDE BLOCK :func+4 fn=4
END :func+4
+++ :func+3 OK
INSIDE BLOCK :func+3 fn=3
END :func+3
+++ :func+2 OK
INSIDE BLOCK :func+2 fn=2
END :func+2
+++ :func+1 OK
INSIDE BLOCK :func+1 fn=1
END :func+1
END Main
As you can see, NEXT 2 will be jumped to by the goto :next in the :cancel function.
Even if I first assumed that the NEXT END at the end of the file would be jumped to

Re: Tricky way to detect calls from another batch script
Posted: 11 Jun 2015 08:26
by dbenham
Re: Tricky way to detect calls from another batch script
Posted: 05 Mar 2016 00:43
by sambul35
siberia-man wrote:http://forum.script-coding.com/viewtopic.php?id=9050
There is fruitful discussion (in Russian) that has began as the report about bug with "goto nonexistent_label" and finished with nice way of detecting the caller script
Code: Select all
if not defined CMDCALLER (
(echo on & goto) 2>nul
call set "CMDCALLER=%%~f0"
call "%~f0" %*
set "CMDCALLER="
)
if /i "%CMDCALLER%" == "%%~f0" set "CMDCALLER="
:: Useful code begins here
echo:caller : %CMDCALLER%
echo:arguments : %*
Unfortunately, the discussion in this thread concentrated on newly discovered (goto) exit from a function, which is important from practical standpoint due to lack of complete functions support in Cmd. However, strictly speaking it went off topic. Can anyone comment on topic in this very interesting thread, in particular:
- give some example when its necessary to "detect calls from another batch script"
- explain how the above code works, i.e. what it actually does?
Re: Tricky way to detect calls from another batch script
Posted: 06 Mar 2016 15:00
by sambul35
I guess one possible use case for this code can be an action upon an event detection. Lets say 2 different batches are running. Once batch 1 detects a certain event, it calls batch 2. Cmd checks if batch 2 is running and if YES blocks 2-nd startup of batch 2. Once the running batch 2 detects the call from batch 1, it executes some code depending on the parameters received.
Similar approach might work if batch 1 is launched by Windows Scheduler, and calls the running batch 2. Once the batch 2 detects the call from batch 1, it does something depending on the parameters received. I guess the batch 2 would need to regularly check if CMDCALLER value is defined, possibly by catching Console output or in a certain file? Are these use cases correct?