Batch file 递归查找被调用的批处理文件

Batch file 递归查找被调用的批处理文件,batch-file,Batch File,我们当前的构建系统是从“主”批处理文件调用的大量批处理文件。如何找到从“主”批处理文件调用的所有批处理文件 不幸的是,存储库还包含许多其他批处理文件,我不能只列出工作副本中的所有批处理文件 我不太在乎“模拟”真正的执行。如果我得到一个可能从主批处理文件调用的所有文件的列表,那就好了 共有299个文件,因此我不希望在每个文件中添加echo“” 引用的批处理文件使用“call xyz.bat”调用,即相对路径。有时批处理文件会更改当前工作目录,如下所示: cd client call mk.bat

我们当前的构建系统是从“主”批处理文件调用的大量批处理文件。如何找到从“主”批处理文件调用的所有批处理文件

不幸的是,存储库还包含许多其他批处理文件,我不能只列出工作副本中的所有批处理文件

我不太在乎“模拟”真正的执行。如果我得到一个可能从主批处理文件调用的所有文件的列表,那就好了

共有299个文件,因此我不希望在每个文件中添加echo“”

引用的批处理文件使用“call xyz.bat”调用,即相对路径。有时批处理文件会更改当前工作目录,如下所示:

cd client
call mk.bat
cd ..


编辑:添加示例

糟糕-由于以下几个原因,这个问题几乎不可能完美解决:

1) 批处理文件可由多种机制“调用”:

  • 调用脚本名

  • 管道,例如:
    scriptName | findstr…

  • 对于/F,例如:
    FOR/F。。。在('scriptName…')中执行…

  • CMD,例如:
    CMD/c scriptName…
    %comspec%/c scriptName

  • 开始,例如
    START”“scriptName…

2) 上述任何构造都可能存在,而不是对批处理脚本的“调用”。例如,CALL可用于调用标签或执行命令。FOR/F可用于解析字符串。等等

3) 无论使用哪种“调用”机制,“调用”目标都可能由变量而不是字符串文本表示。例如:

set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
set "myCommand=CALL"
%myCommand% ScriptName
4) 脚本名称和路径可能不会出现在代码中。它可以从文件系统动态读取,也可以通过逻辑派生

5) 实际调用本身可以作为值嵌入到变量中。对于任何调用机制都是如此。例如:

set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
set "myCommand=CALL"
%myCommand% ScriptName
6) 正如您在问题中所指出的,脚本的路径可能是相对的,并且脚本可能会更改当前目录。或者“调用”可能依赖于PATH环境变量

7) 任何“被调用”的脚本本身都可以“调用”另一个脚本


您可以使用FINDSTR查找任何调用机制,您可能会找到大多数“调用”。但很可能会有很多误报。您可以添加
/N
开关,在每个匹配行前面加上行号。然后,您需要在文本编辑器中手动检查每个匹配行,以查看它是否是您感兴趣的“调用”

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

可能存在太多误报,因此最好手动跟踪整个脚本的逻辑:-(尤其如此,因为不能保证FINDSTR会找到所有“调用”,因为调用本身可以隐藏在变量后面。

糟糕-这个问题几乎不可能完美解决,原因有几个:

1) 批处理文件可由多种机制“调用”:

  • 调用脚本名

  • 管道,例如:
    scriptName | findstr…

  • 对于/F,例如:
    FOR/F。。。在('scriptName…')中执行…

  • CMD,例如:
    CMD/c scriptName…
    %comspec%/c scriptName

  • 开始,例如
    START”“scriptName…

2) 上述任何构造都可能存在,而不是对批处理脚本的“调用”。例如,CALL可用于调用标签或执行命令。FOR/F可用于解析字符串。等等

3) 无论使用哪种“调用”机制,“调用”目标都可能由变量而不是字符串文本表示。例如:

set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
set "myCommand=CALL"
%myCommand% ScriptName
4) 脚本名称和路径可能不会出现在代码中。它可以从文件系统动态读取,也可以通过逻辑派生

5) 实际调用本身可以作为值嵌入到变量中。对于任何调用机制都是如此。例如:

set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
set "myCommand=CALL"
%myCommand% ScriptName
6) 正如您在问题中所指出的,脚本的路径可能是相对的,并且脚本可能会更改当前目录。或者“调用”可能依赖于PATH环境变量

7) 任何“被调用”的脚本本身都可以“调用”另一个脚本


您可以使用FINDSTR查找任何调用机制,您可能会找到大多数“调用”。但很可能会有很多误报。您可以添加
/N
开关,在每个匹配行前面加上行号。然后,您需要在文本编辑器中手动检查每个匹配行,以查看它是否是您感兴趣的“调用”

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

可能存在太多误报,因此最好手动跟踪整个脚本的逻辑:-(尤其如此,因为不能保证FINDSTR会找到所有“调用”,因为调用本身可以隐藏在变量后面。

您可以预先记录到每个批处理文件。我知道您说过您不想这样做,但这并不像您想象的那么难

@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
@echo关闭
::addlogging.bat补丁|取消补丁
::
::addlogging.bat补丁
::查找当前目录和每个子目录中的每个.bat文件
::beyond和修补程序,在日志文件中预先添加回显
::
::addlogging.bat unpatch
::查找当前目录和每个子目录中的每个.bat文件
::beyond,并删除日志记录修补程序
setlocal
如果#%1==#修补程序转到下一步
如果#%1==#取消匹配转到下一步
转到使用
:下一个
对于/f“delims=“%%I in('dir/s/b*.bat')do(
如果不是“%%I”==“%~f0”(
如果#%1==#补丁(
set/p I=“修补%%I..”^>“%%userprofile%%\Desktop\%%日期:/=-%%.log”>“%%~dpnI.tmp”
)否则(