Search code examples
windowsbatch-filecmdparameter-passingmethod-call

CALL on files with special symbols fail


I was trying to do some operations to every file in a folder.

for %%d in (*.txt) do call:test "%%d"
pause
exit /B

:test
echo %1

Usually this works fine, but if there is a file %~aaa.txt in the folder, it says that The following usage of the path operator in batch-parameter substitution is invalid: %~aaa.txt.

How to make it handle this correctly? Maybe some unescaping?


Solution

  • The issue is that the call command parses the command line call:test "%%d" (which should read call :test "%%~d") a second time:

    1. At first, %%d becomes expanded to the currently iterated file, which is %~aaa.txt in the failing situation.
    2. The expression %~aaa.txt now becomes parsed another time due to the call command, where %~ is the beginning of an argument reference, the following a is a modifier (~a would expand to file attributes), but there is the decimal digit missing (%~a1 or %~aaa2 were valid, for instance).

    To work around that, you could put the argument into a normal environment variable and read it in the sub-routine (I used delayed variable expansion therein in order to avoid troubles with special characters):

    for %%d in (*.txt) do (
        set "ARG=%%~d"
        call :test
    )
    exit /B
    
    :test
    setlocal EnableDelayedExpansion
    echo(!ARG!
    endlocal
    exit /B
    

    You could also pass the variable name as an argument to the sub-routine:

    for %%d in (*.txt) do (
        set "ARG=%%~d"
        call :test ARG
    )
    exit /B
    
    :test
    setlocal EnableDelayedExpansion
    echo(!%~1!
    endlocal
    exit /B
    

    Another way is to let call expand the actual file name during its second parsing phase:

    for %%d in (*.txt) do (
        set "ARG=%%~d"
        call :test "%%ARG%%"
    )
    exit /B
    
    :test
    set "STR=%~1"
    setlocal EnableDelayedExpansion
    echo(!STR!
    endlocal
    exit /B
    

    To avoid issues with file names containing ^, & or other special characters, a simple echo(%~1 in the sub-routine is avoided.