Spaces in batch script arguments

batch-filecmd

I have a batch script which needs to perform an action on each of its arguments. Each argument is a file name (there are not switches), and of course file names may contain spaces. The batch script is run either by dragging files into the .bat icon in Explorer or by entering the files at the command line, enclosing arguments with spaces in quotes.

Within the batch script, there are problems with handling arguments with spaces. If I use %* as follows, the quotations are ignored and each 'word' between spaces is treated as an argument.

for %%x in (%*) do (
    echo %%x
)

I have also tried using shift, which doesn't seem to work right either, choking on files with spaces in their name:

:next
if not %1 == "" (
    echo %1

    shift /1
    goto next
)

What is the ideal way to iterate through all arguments?

In Bash, one would simply use "$@" and everything Just Works™, but of course that doesn't seem to be the case with Windows batch scripts.

Best Answer

The substitution modifiers for for variable references also allow for using the ~ expansions. See for command reference.

By using "%%~x" you should get a properly quoted parameter, similar to how bash handles "$@".

@echo off

setlocal enableextensions

for %%x in (%*) do (
    echo "%%~x"
)

The characters , and ; can be used to separate command parameters. See command shell overview. Thus you have to put quotes around file names that contain these characters.

If you drag a file from the Explorer onto the .bat, Explorer will only quote the file correctly if it has a white space character in its path. E.g., D:\a,b,c.exe will not be quoted by Explorer and thus will be parsed as three separate arguments by cmd.exe.

To make the script work with drag and drop from the Explorer for these freak cases, you can use the following (ugly) work-around:

@echo off

setlocal enableextensions enabledelayedexpansion

set "args=%*"
set "args=%args:,=:comma:%"
set "args=%args:;=:semicolon:%"

for %%x in (%args%) do (
    set "filepath=%%~x"
    set "filepath=!filepath::comma:=,!"
    set "filepath=!filepath::semicolon:=;!"
    echo "!filepath!"
)

The script introduces a helper variable args, where each occurrence of a troublesome character is replaced with a placeholder (note that the colon character itself cannot be used in a file name under Windows).

The body of the for loop uses another helper variable filepath which undos the transformation to produce the original path.