Search code examples
windowsbatch-filecmdcommand-prompt

How to dynamically print exclamation mark from a variable while using Delayed Expansion and Loops in CMD?


I have been trying to print filenames and some of them contain "!" within their names. The current output I have, it prints the filenames without the "!".

I tried to Enable and Disable Delayed Expansion inside the loop but that seems to throw some errors.

ERROR: "Maximum setlocal recursion level reached."

A snippet of my batch:

SETLOCAL EnableDelayedExpansion

FOR /f "tokens=*" %%A IN ('dir /B "%covers_dump_full_path%" ^| findstr /V /I /C:".html"') DO (

    SET "game_name=%%A"

    FOR %%F IN ("%%A") DO SET "game_name_no_ext=%%~NF"

    if !current_column! == 1 (
        ECHO         ^<tr^>>> "%game_html%"
    )

    REM -------- THIS IS THE LINE THAT IS CAUSING ME ISSUES -------- 
    ECHO             ^<td align="center"^> ^<img src="!game_name!" width=300 height=250^> ^<br^> ^<b^> !game_name_no_ext! ^<br^> ^</b^> ^</td^>>> "%game_html%"

    if !current_column! == !max_columns! (
        ECHO         ^</tr^>>> "%game_html%"
        SET /a current_column=0
    )

    SET /a current_column=!current_column!+1
)

So, how can I print the filenames even if they have "!" within their names?

I had an idea on my mind to use Hybrid Scripting with VBScript to resolve this issue. But I'm trying to make it as a full pure CMD batch script. I don't want to rewrite my batch to include VBScript unless there are no solutions whatsoever.


Solution

  • @ECHO OFF
    SETLOCAL ENABLEDELAYEDEXPANSION
    rem The following setting for the directory is a name
    rem that I use for testing and deliberately includes spaces to make sure
    rem that the process works using such names. These will need to be changed to suit your situation.
    
    SET "sourcedir=u:\your files"
    
    SET /a current_column=1
    SET /a max_columns=3
    SET "game_html=u:\g.txt"
    
    (
    
    FOR /f "tokens=*" %%E IN ('dir /B /a-d "%sourcedir%" ^| findstr /V /I /C:".html"') DO (
    
        if !current_column! == 1 (
            ECHO         ^<tr^>
        )
    
        SETLOCAL DISABLEDELAYEDEXPANSION 
    
        REM -------- THIS IS THE LINE THAT IS CAUSING ME ISSUES -------- 
        ECHO             ^<td align="center"^> ^<img src="%%E" width=300 height=250^> ^<br^> ^<b^> %%~nE ^<br^> ^</b^> ^</td^>
    
        ENDLOCAL 
    
        if !current_column! == %max_columns% (
            ECHO         ^</tr^>
            SET /a current_column=0
        )
    
        SET /a current_column+=1
    )
    
    ECHO         ^</tr^>
    
    )>> "%game_html%"
    
    GOTO :EOF
    

    Always verify against a test directory before applying to real data.

    Just a little rewrite.

    I've changed the metavariable from %%F to %%E for reasons that are controversial but make sense to me.

    Added /a-d to the dir command to exclude directory names matching the mask; mask to suit my machine.

    Included setlocal/endlocal to temporarily disable delayedexpansion.

    Used %%E and %%~nE instead of variables, so these no longer need to be established.

    max_column does not change within the loop, so %var% used. Using this convention yells Danger, Will Robinson! about variables that do change in the loop but keeps quiet about those that don't.

    Used shorter increment method for current_column (see set /? from the prompt for documentation)

    Added (..)>>filename around the entire for which accumulates the echoed items and appends them to the file, hence added the required terminal </tr>