kossmoboleat
kossmoboleat

Reputation: 1941

Find called batch files recursively

Our current build system is a mass of batch files that are called from a "master" batch file. How can I find all the batch files that are called from the "master" batch file?

Unfortunately the repository also contains a lot of other batch files and I cannot just list all the batch files in the working copy.

I don't care much about "simulating" a real execution. It would be fine if I got a list of all the files potentially called from the master batch file.

There are 299 files, so I'd rather not add an echo "" to each one of them.

Referenced batch files are called using "call xyz.bat", i.e. relative paths. Sometimes the batch files change the current working directory though, like so:

cd client
call mk.bat
cd ..

or

pushd client\install
call prepare.bat
popd

EDIT: added examples

Upvotes: 0

Views: 470

Answers (2)

rojo
rojo

Reputation: 24466

You could prepend logging to each of the batch files. I know you said you'd rather not, but it's not as hard as you might think.

@echo off
:: addlogging.bat patch|unpatch
::
:: addlogging.bat patch
::    finds every .bat file in the current directory and every subdir
::    beyond, and patches each, prepending an echo to a log file
::
:: addlogging.bat unpatch
::    finds every .bat file in the current directory and every subdir
::    beyond, and removes the logging patch

setlocal

if #%1==#patch goto next
if #%1==#unpatch goto next
goto usage

:next
for /f "delims=" %%I in ('dir /s /b *.bat') do (
    if not "%%I"=="%~f0" (
        if #%1==#patch (
            set /p I="Patching %%I... "<NUL
            echo @echo %%time%% %%~f0 %%* ^>^> "%%userprofile%%\Desktop\%%date:/=-%%.log">"%%~dpnI.tmp"
        ) else (
            set /p I="Unpatching %%I... "<NUL
        )
        findstr /v "^@echo.*time.*~f0.*>>" "%%I">>"%%~dpnI.tmp"
        move /y "%%~dpnI.tmp" "%%I">NUL
        echo Done.
    )
)
goto :EOF

:usage
echo usage: %~nx0 patch^|unpatch

Upvotes: 1

dbenham
dbenham

Reputation: 130819

Yuck - this problem is nearly impossible to solve perfectly for several reasons:

1) Batch files may be "called" by multiple mechanisms:

  • CALL scriptName

  • pipes, for example: scriptName | findstr ...

  • FOR /F, for example: for /f ... in ('scriptName ...') do ...

  • CMD, for example: cmd /c scriptName ... or %comspec% /c scriptName

  • START, for example start "" scriptName ...

2) Any of the above constructs may be present without being a "call" to a batch script. For example, CALL can be used to call a label, or to execute a command. FOR /F could be used to parse a string. etc.

3) No matter which "call" mechanism is used, the "call" target may be represented by a variable instead of the string literal. For example:

set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%

4) The script name and path might not appear in the code. It could be dynamically read from the file system, or derived via logic.

5) The actual call itself may be embedded as a value in a variable. This is true for any call mechanism. For example:

set "myCommand=CALL"
%myCommand% ScriptName

6) As you have noted in your question, the path to the script may be relative, and the script may change the current directory. Or the "call" may be relying on the PATH environment variable.

7) Any "called" script could itself "call" another script.


You could use FINDSTR to look for any of the call mechanisms, and you will likely find most of the "calls". But there will likely be many false positives. You could add the /N switch to prefix each matching line with the line number. Then you would need to check each matching line manually in your text editor to see if it is a "call" that you are interested in.

findstr /nir /c:"\<call\>" /c:"|" /c:"for  */f " /c:"\<cmd\>" /c:"\<%comspec%\>" /c:"\<start\>" *.bat

There may be so many false positives that you might be better off manually tracing the logic of the entire script :-( This is especially true since there is no guarantee that FINDSTR will find all "calls", since the call itself could be masked behind a variable.

Upvotes: 2

Related Questions