为什么批处理文件中的 FOR /L 命令不能按预期工作?

3

我有一个批处理文件:

:extract_stream
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "delims=" %%A IN ('%media_info% "--output=%stream_count_string%" "%file%"') DO SET /A "stream_type_total=%%A"

SET /A "stream_count=1"

FOR /F "tokens=1-4 delims=," %%A IN ('%media_info% "--output=%format_string%" "%file%"') DO (
    SET /A "id=%%A-1"
    SET "title=%%B"
    SET "codec=%%C"
    SET "language=%%D"

    REM SET "id=!id:~1!"
    SET "title=!title:~1!"
    SET "codec=!codec:~1!"
    SET "language=!language:~1!"

    IF NOT DEFINED language SET "language=English"
    REM IF "!codec!" EQU "V_MPEG4/ISO/AVC" SET "language="

    SET stream_obj[!stream_count!].id=!id!

    ECHO ID:!id! Title=!title!, Codec=!codec!, Lamguage=!language!

    SET /A "stream_count+=1"
)
(  ENDLOCAL
    FOR /L  %%A IN (1,1,%stream_type_total%) DO (
        CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%"
    )
)
GOTO:EOF

但是我在使用 FOR /L 循环时,无法如预期地获取到 %%A

如果我使用数字 1 而不是 %%A ,则可以正常工作。

SET "stream_obj[1].id=%stream_obj[1].id%"

但是在 FOR /L 循环中的命令行无法与以下内容一起使用:

SET "stream_obj[%%A].id=%stream_obj[%%A].id%"   

为什么?

当我使用 echo 进行检查时,它在等号 = 处把一行文本分成了奇怪的部分。所以我不知道它是否与此有关。


CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%" 中,%stream_obj[%%A].id%" 部分始终为空,因为百分号扩展尝试查找名为 stream_obj[%%A].id 的变量,而 %%A 的扩展比百分号扩展晚。但即使您加倍百分号,它也会失败,因为 ENDLOCAL 已经删除了变量。 - jeb
2个回答

2
不要使用算术表达式来为环境变量赋值。通常没有这样做的理由。只需使用 SET "stream_type_total=%%A"而不需要/A
命令GOTO和第一个参数:EOF之间应该有一个空格。
但是主要问题在于命令块:
(  ENDLOCAL
    FOR /L  %%A IN (1,1,%stream_type_total%) DO (
        CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%"
    )
)

命令ENDLOCAL不仅恢复延迟环境变量扩展的先前状态,而且还放弃当前活动环境变量列表并恢复先前的环境变量列表。有关命令SETLOCAL和ENDLOCAL的详细信息,请阅读此答案。因此,在此命令之后,环境变量stream_type_total以及所有stream_obj[x].id环境变量都不存在了。
我建议修改此命令块为:
FOR /L %%A IN (1,1,%stream_type_total%) DO CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%"
ENDLOCAL

现在FOR循环应该按预期工作。

但请注意,即使进行了此更改,在ENDLOCAL命令之后修改的stream_obj[x].id环境变量也不再存在。

因此,最好完全避免在此子例程中使用SETLOCALENDLOCAL,可以使用以下代码:

:extract_stream
FOR /F "delims=" %%A IN ('%media_info% "--output=%stream_count_string%" "%file%"') DO SET "stream_type_total=%%A"

SET "stream_count=1"

FOR /F "tokens=1-4 delims=," %%A IN ('%media_info% "--output=%format_string%" "%file%"') DO (
    SET /A "id=%%A-1"
    SET "title=%%B"
    SET "codec=%%C"
    SET "language=%%D"

    REM SET "id=!id:~1!"
    CALL SET "title=%%title:~1%%"
    CALL SET "codec=%%codec:~1%%"
    IF DEFINED language CALL SET "language=%%language:~1%%"

    IF NOT DEFINED language SET "language=English"
    REM IF "!codec!" EQU "V_MPEG4/ISO/AVC" SET "language="

    CALL SET "stream_obj[%%stream_count%%].id=%%id%%"

    CALL ECHO ID:%%id%% Title=%%title%%, Codec=%%codec%%, Language=%%language%%

    SET /A "stream_count+=1"
)

FOR /L %%A IN (1,1,%stream_type_total%) DO CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%"
GOTO :EOF

在上面的代码中,被注释为REM命令的部分没有被修改。

另一种解决方案是使用一个额外的子程序:

:extract_stream
FOR /F "delims=" %%A IN ('%media_info% "--output=%stream_count_string%" "%file%"') DO SET "stream_type_total=%%A"

SET "stream_count=1"

FOR /F "tokens=1-4 delims=," %%A IN ('%media_info% "--output=%format_string%" "%file%"') DO CALL :UpdateVariables "%%~A" "%%~B" "%%~C" "%%~D"

FOR /L %%A IN (1,1,%stream_type_total%) DO CALL SET "stream_obj[%%A].id=%stream_obj[%%A].id%"
GOTO :EOF

:UpdateVariables
SET /A "id=%~1-1"
SET "title=%~2"
SET "codec=%~3"
SET "language=%~4"

REM SET "id=%id:~1%"
SET "title=%title:~1%"
SET "codec=%codec:~1%"
IF DEFINED language SET "language=%language:~1%"

IF NOT DEFINED language SET "language=English"
REM IF "%codec%" == "V_MPEG4/ISO/AVC" SET "language="

SET "stream_obj[%stream_count%].id=%id%"

ECHO ID:%id% Title=%title%, Codec=%codec%, Language=%language%

SET /A "stream_count+=1"
GOTO :EOF

被用 REM 命令注释掉的命令在上面的代码中进行了修改。

最后一个提示:不要使用比较运算符EQU来比较两个字符串。该运算符是设计用于比较32位有符号整数值,而不是用于字符串比较。阅读Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files上的答案以了解有关使用< strong> IF 命令比较字符串或整数值的详细信息。


0

如果您尝试从setlocal范围中获取多个值,则无法使用循环/调用方式进行尝试。

我看到了两种不同的解决方案。
1)通过离开setlocal范围并重新进入它来在每个循环中拯救一个值。 但是,您还会失去所有其他变量,例如stream_count

2)使用endlocal块,并为所有变量使用一个聚合变量进行拯救。 像这样

set "rescue=%stream_obj[1].id%:%stream_obj[2].id%:%stream_obj[3].id%"

另请参见
退出函数并保留跨endlocal障碍的变量
跨endlocal障碍返回多个变量的宏


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