Tricky way to detect calls from another batch script

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Tricky way to detect calls from another batch script

#1 Post by siberia-man » 07 Jun 2015 09:23

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

@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 : %*

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Tricky way to detect calls from another batch script

#2 Post by dbenham » 07 Jun 2015 10:34

:shock: 8) Very cool and elegant

Aacini
Expert
Posts: 1913
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Tricky way to detect calls from another batch script

#3 Post by Aacini » 09 Jun 2015 08:21

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! :shock: Check this:

FirstOne.bat

Code: 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.bat

Code: 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.bat

Code: 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.bat

Code: 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.bat

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

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Tricky way to detect calls from another batch script

#4 Post by jeb » 09 Jun 2015 09:47

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 :D
Currently the GetLineNumber doesn't need this trick for a single file,
but with the GOTO trick this can even work over multiple files. :D

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Tricky way to detect calls from another batch script

#5 Post by jeb » 10 Jun 2015 15:32

I played today a bit with the new (goto) technic and I discovered two important points :D

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 :D
One I have prepared today, I will show at Exception Handling with batch using erroneous GOTO

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

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Tricky way to detect calls from another batch script

#6 Post by dbenham » 11 Jun 2015 08:26

:o Fantastic :!: :!: :!: :!: :!: :!: :!: :!:

All these new discoveries really open up a whole new world of possibilities :D


Dave Benham

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Tricky way to detect calls from another batch script

#7 Post by sambul35 » 05 Mar 2016 00:43

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?

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Tricky way to detect calls from another batch script

#8 Post by sambul35 » 06 Mar 2016 15:00

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?

Post Reply