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