Search code examples
windowsbatch-filefor-loopfile-attributes

Windows Batch, for loop and file attributes while calling another batch


I recently started converting all my video files to MP4 for streaming purposes, and was using batch so far without issues. Now I have 6000+ videos to convert and needed some sort of queue system I could manipulate.

This is my current code to convert everything from directories into commands in a separate folder.

@echo off
setlocal disabledelayedexpansion
set dirs=D:\ANIME E:\ANIME F:\ANIME
for %%a in (%dirs%) do (
    echo %%a
    pushd %%a
    for /f "tokens=* delims= " %%f IN ('dir/b/s *.avi *.mkv *.m4v *.mpeg *.mpg') do (
        echo D:\handbrake\HandBrakeCLI.exe -i "%%f" -e qsv_h264 -q 20 -E fdk_haac -B 196 -R Auto -D 0,0,0,0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback copy:* --subtitle=1 --subtitle-default --subtitle-forced --subtitle-burn -o "%%~dpf%%~nf.mp4" > "D:\batch\%%~nf.bat"
        echo del /F "%%f" >> "D:\batch\%%~nf.bat"
    )
)

This works great and makes me 1000's of bat files to call and that is where the issue arises. A lot of files have special characters like ! in their names which messes up the for loop I use to loop through these. I set the file to read-only before processing, so my other batch won't try to convert the same file (I run 3 at once).

I found a workaround using ENABLEDELAYEXPANSION, but after a few loops I will get the error saying I exceeded the maximum calls for that.

@echo off
setlocal enableextensions 
echo Starting loop
goto loop

:loop
for  %%f in (D:\batch\*.bat) do (
    set ATTRIBS=%%~af
    setlocal enabledelayedexpansion
    set READ_ATTRIB=!ATTRIBS:~1,1!
    if !READ_ATTRIB!==- (
        setlocal disabledelayedexpansion
        attrib +R "%%f"
        call "%%f"
    )

    timeout /t 2 /nobreak
    goto loop
)
endlocal

Normally you'd say just change the ! variables to % and remove delayedexpansion, but that gives me errors in getting the file attributes.

set READ_ATTRIB=%ATTRIBS:~1,1%
        if %READ_ATTRIB%==- (

Will give me no result and the error ( is unexpected If I echo READ_ATTRIB the result will be ~1,1 always and when echoing others I will get ECHO IS OFF.

Is there any way to not use delayedexpansion or in a way that I won't get errors after a few loops?


Solution

  • 32 is the deepest level of setlocal when used in batch file. You need to use setlocal - endlocal pair analogously to a pair of ( left and ) right parentheses. See capitalized ENDLOCAL below.

    @echo off
    setlocal enableextensions 
    echo Starting loop
    goto loop
    
    :loop
    for  %%f in (D:\batch\*.bat) do (
        set ATTRIBS=%%~af
        setlocal enabledelayedexpansion
        set READ_ATTRIB=!ATTRIBS:~1,1!
        if !READ_ATTRIB!==- (
            setlocal disabledelayedexpansion
            attrib +R "%%f"
            call "%%f"
            ENDLOCAL
        )
        ENDLOCAL
        timeout /t 2 /nobreak
        REM                    this was totally mazy:  goto loop
    )
    endlocal
    

    Moreover, latter goto loop in your original code causes that for %%f loop is initialised over and over indefinitely against the same data set, i.e. %%f in the loop body refers to the only file all the time. See capitalized REM.