Reputation: 90012
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.
Upvotes: 3
Views: 10910
Reputation: 65751
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.
Upvotes: 3
Reputation: 358
I had a similar issue with file names that contain equal signs (=), which causes the file name to be split into multiple arguments. I solved it by using "%*"
.
If you have a file with spaces, e.g. foo bar baz.txt
, this will be quoted twice: ""foo bar baz.txt""
. Now the double double-quotes are escaped: foo bar baz.txt
, resulting in %1
= foo
, %2
= bar
, and %3
= baz.txt
. So this does not work.
If you have a file with spaces AND/OR equal signs, you can use:
set input=""%*""
set input=%input:"=%
your_program "%input%"
Now, foo bar=baz.txt
will be quoted thrice: """foo bar=baz.txt"""
. Two quotes will be escaped and input
becomes "foo bar=baz.txt"
. With the second line, double-quotes are replaced by nothing (removed). You need to put the quotes around input
again when you enter it into your_program
, otherwise it will see spaces as separate input!
If you only have equal signs, then ""%*""
makes foo=bar=baz.txt
into ""foo=bar=baz.txt""
, which enters your program as %1
= foo=bar=baz.txt
.
Upvotes: 0