batch-filecmd

How to prevent the FOR command stripping commas from filenames in a batch file? (Windows 11 command line)


When I pass a filename containing a comma to a batch file, and then attempt to process that filename using the FOR command, the FOR command removes the comma from the filename. Sample batch file:

echo %1
for /f "delims=`" %%i in ("%1") do echo "%%~fi"

If I pass a filename of Hartford, CT.txt to the above batch file, I would expect the output to be:

Hartford, CT.txt
Hartford, CT.txt

But the FOR command appears to remove the comma, and so the actual output of the batch file is:

Hartford, CT.txt
Hartford CT.txt

I am thinking it has something to do with delimiters, and I've read lots of articles & posts about this command - but have been unable to find a solution to this. If I remove the "delims=`" from the command, the comma is used to delimit the filename and the output is simply (and as expected):

Hartford

So even when a different delimiter is specified, it seems the command is swallowing the comma. is there any way to prevent this behavior?

EDIT: For @Compo I am adding a screenshot demonstrating the issue:

enter image description here


Solution

  • The help output on running cmd /? in a command prompt window explains that a file name or folder name or any other argument string passed to a Windows command or an executable or script like a batch file containing a space or one of these characters &()[]{}^=;!'+,`~ must be enclosed in ".

    The file name C:\Temp\Hartford, CT.txt contains a comma and a space character.

    The Windows Command Processor interprets following characters as argument string separators on not being inside a string with surrounding ":

    1. The normal space character with code point value 32 (hexadecimal 0x20).
    2. The comma character , with code point value 44 (hexadecimal 0x2C).
    3. The semicolon character ; with code point value 59 (hexadecimal 0x3B).
    4. The equal sign character = with code point value 61 (hexadecimal 0x3D).
    5. The OEM encoded no-break space with code point value 255 (hexadecimal 0xFF).

    One or more of these characters outside an argument string enclosed in " are interpreted like a single normal space, except there is before the caret character ^ with code point value 94 (hexadecimal 0x5E) which escapes the next character for being interpreted literally. Please note that ^ is only outside a double quoted argument string interpreted as escape character. The character ^ is interpreted literally inside a string beginning with " and ending with ".

    The help output on running call /? in a command prompt window explains how argument strings like a file name passed to a batch file can be referenced inside the batch file.
    COMMAND.COM supported only %1, %2, %3 … for referencing the first, second, third … argument string as passed to the batch file.
    cmd.exe offers referencing the batch file arguments with usage of modifiers with enabled command extensions. The most often used modifier in batch files is just ~ like with %~1 to reference the first passed argument string with removal of the double quote character at the beginning and at the end of the argument string if the passed argument string has the character " at the beginning at all.

    Note: If the argument string passed to a batch file does not begin with " but has a double quote character at the end, cmd.exe does not remove " from the end of the argument string on using %~1. That results usually very fast in a serious syntax error in a command line of the batch file reported by cmd.exe with an error message before processing of the batch file is exited next.

    A file name like C:\Temp\Hartford, CT.txt must be passed to a batch file with:
    "C:\Temp\Hartford, CT.txt"

    The first argument string referenced with %1 without a modifier is therefore
    "C:\Temp\Hartford, CT.txt" with the two double quotes.

    The general solution in a batch file is referencing a passed file/folder name always with explicitly enclosing the string in double quotes and letting cmd.exe remove perhaps already existing surrounding double quotes from the argument string.

    What is wrong on this command line?

    for /f "delims=`" %%i in ("%1") do echo "%%~fi"
    

    The parsing of this command line results in:

    for /F "delims=`" %i in (""C:\Temp\Hartford CT.txt"") do echo "%~fi"
    
    1. The lowercase FOR option /f becomes the uppercase option /F.
    2. The loop variable reference %%i becomes %i.
    3. %1 is replaced first by the first passed argument string "C:\Temp\Hartford, CT.txt" with the double quotes and next by "C:\Temp\Hartford CT.txt".
    4. The loop variable reference with full name modifier %%~fi becomes %~fi.

    What has happened with the comma?

    There is inside the round brackets initially ""C:\Temp\Hartford, CT.txt"". The Windows Command Processor processes the command line from left to right. It encounters the first " and enables internally the parsing rule for a double quoted string. But the next character is once again " which disables internally the parsing rule for a double quoted string. The next characters are parsed therefore with interpreting the comma and the space as argument string separators. The two argument string separators are replaced for that reason by a single space as that is enough to separate the argument string ""C:\Temp\Hartford from the argument string CT.txt"".

    The further processing by for /F and %%~fi is not as expected now.

    The help output on running for /? describes for which purposes a for /F loop can be used:

    1. Processing of lines read from a text file.
    2. Processing a single string which can contain also multiple double quotes.
    3. Processing the lines output to handle STDOUT (standard output) of a command line executed by one more cmd.exe started in background with %ComSpec% /c and the specified command line appended as arguments captured by cmd.exe processing the batch file with the for /F loop and processing these lines after the cmd.exe process started in background closed itself after finishing the command line execution.

    A for /F loop is not needed at all in this case. A simple FOR loop is enough. Important is to let cmd.exe remove the surrounding " from the referenced argument string by using %~1 instead of %1 as the required surrounding " are part of the command line in the batch file.

    for %%i in ("%~1") do echo %%~fi
    

    Well, the same output can be achieved with just echo %~f1 as the fully qualified file/folder name modifier ~f can be used also on referencing the first argument string.