批处理文件中嵌套循环的作用域问题

3
我有一个在批处理文件中异步执行的SSIS作业。
我需要知道 SSIS 作业何时完成输出一堆 PDF 和 XLS 文件。
这些文件出现在两个目录中,首先是PDF,其次是XLS。
我选择编写第二个批处理文件,在 SSIS 作业退出后等待一会儿,然后检查目录中最后一个写入的文件是否已经在那里存在了3分钟,这样做可以充分考虑到作业写入文件的时间。
问题是:如果内部循环迭代超过一次,则外部循环永远不会运行。这似乎表明第二次声明MYPATH时,n的值是错误的,但这不可能是真的,因为脚本返回1而不是在检查Arr[badval]时失败。
@ECHO OFF

REM SSIS process is asyncronous, and executes in background. Problematic for
REM DAG, which relies on exit code to understand process.
REM Check for "last file written" in output directories every N seconds.
REM It's a good bet we are done when they match.

@setlocal enabledelayedexpansion

REM "sleep" wait for non-existent command to complete
waitfor ragnarok /t 180>NUL 2>&1

set Arr[0]=E:\pdf_output
set Arr[1]=E:\xls_output

for /l %%n in (0,1,2) do (

    if defined Arr[%%n] (
        REM set value for path within loop or scope will bite you
        set MYPATH=!Arr[%%n]!
        echo Checking file ages in !MYPATH!.
    ) else (
        echo Done
        EXIT /B 0
    )

    :while1
        REM don't put a blank line here, it throws a syntax error
        FOR /F "delims=|" %%I IN ('DIR !MYPATH! /B /O:D') DO SET FILE1=%%I

        REM "sleep" use ping for delay, since waitfor will break loop
        arp -s 192.168.1.254 >nul
        ipconfig /flushdns >nul
        ping localhost -n 180 >nul

        FOR /F "delims=|" %%I IN ('DIR !MYPATH! /B /O:D') DO SET FILE2=%%I

        if NOT "!FILE1!" == "!FILE2!" (
            goto :while1
        )
)
endlocal
REM Something is wrong, return 1 to stop DAG and invite inspection.
exit /B 1

你不能中断 FOR /L 命令,并且你永远不应该在 FOR 命令内使用 GOTO - Squashman
问题在于 goto :while1,因为它会打破所有嵌套在其中的循环的循环上下文;因此,您必须将整个 WHILE 循环结构放入子例程中,并通过 call 调用它... - aschipfl
@aschipfl:你的建议似乎解决了问题。感谢你表达得清晰明了。 - Foo Barberger
@aschipfl:有一件事让我困惑的是,为什么for /L循环不遵守if defined Arr[%%n]的检查?这似乎相当奇怪。 - Foo Barberger
你只设置了 Arr[0]Arr[1],但是你的 for /L 循环迭代到索引 2,因此最后一次迭代指向未定义的变量 Arr[2];你应该像这样进行判断 if exist "Arr[%%n]" 以避免出现空值(还有特殊字符)的问题... - aschipfl
1个回答

2
按照@aschipfl的建议将while循环拆分为子程序似乎解决了问题:
@ECHO OFF

REM SSIS process is asyncronous, and executes in background. Problematic for
REM DAG, which relies on exit code to understand process.
REM Check for "last file written" in output directories every N seconds.
REM It's a good bet we are done when they match.

@setlocal enabledelayedexpansion

REM "sleep" wait for non-existent command to complete
waitfor ragnarok /t 120>NUL 2>&1

set Arr[0]=E:\pdf_output
set Arr[1]=E:\xls_output

for /l %%n in (0,1,2) do (

    if defined Arr[%%n] (
        REM set value for path within loop or scope will bite you
        set MYPATH=!Arr[%%n]!
        echo Checking file ages in !MYPATH!.
        CALL :checkfiles
    ) else (
        echo Done
        goto :NormalExit
    )
)

:checkfiles
    :while1
        REM don't put a blank line here, it throws a syntax error
        FOR /F "delims=|" %%I IN ('DIR !MYPATH! /B /O:D') DO SET FILE1=%%I

        REM "sleep" use ping for delay, since waitfor will break loop
        arp -s 192.168.1.254 >nul
        ipconfig /flushdns >nul
        ping localhost -n 120 >nul

        FOR /F "delims=|" %%I IN ('DIR !MYPATH! /B /O:D') DO SET FILE2=%%I

        if NOT "!FILE1!" == "!FILE2!" (
            goto :while1
        )
endlocal

:NormalExit
    exit /B 0

REM Something is wrong, return 1 to stop DAG and invite inspection.
exit /B 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接