Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Batch file with spaces in variables/filenames in text file

I'm not at all educated on coding so you'll have to excuse any poor terminology!

I've picked a few pieces of code for a batch file from around the web (some of it from this site) and I've attempted to rewrite some of it to suit my needs.

It creates a new directory, then looks through another directory and all its subdirectories for filenames in a .txt list, and then copies those files to the directory created at the beginning. Any files that are not found from the list are written into another text file to keep track of anything that is missing.

The code is:

md 00FoundImages

set LIST=%cd%\imagelist.txt
set FILESPATH=%cd%\testimages
set DEST=%cd%\00FoundImages

for /f "delims=" %%x in (%LIST%) do (forfiles /p %FILESPATH% /s /m %%x* /c "cmd /c move /y @path %DEST%\@file" 2>>errors.txt)

This works fine under most conditions, except for when:

  1. A filename in the text file contains a space
  2. The path to the directory in which the batch file resides contains a space (or other special character)

I've tried enclosing in quotes like so:

set FILELIST="%cd%\imagelist.txt"
set FILESPATH="%cd%\testimages"
set DESTPATH="%cd%\00FoundImages"

This fails at the forfilescommand as soon as a space is found in the directory name.

I've tried enclosing the variable when it is used in quotes like so:

for /f "delims=" %%x in ("%LIST%") do (forfiles /p "%FILESPATH%" /s /m %%x* /c "cmd /c move /y @path "%DEST%\@file"" 2>>errors.txt)

When I run the batch file from a directory called 'testing - Copy', I get the error written into my errors.txt file

ERROR: Invalid argument/option - '-'.
Type "FORFILES /?" for usage.

This is the print of what is occurring:

E:\ImageAutomationStuff\testing - Copy>md 00FoundImages
A subdirectory or file 00FoundImages already exists.

E:\ImageAutomationStuff\testing - Copy>set LIST=E:\ImageAutomationStuff\testing - Copy\imagelist.txt

E:\ImageAutomationStuff\testing - Copy>set FILESPATH=E:\ImageAutomationStuff\testing - Copy\testimages

E:\ImageAutomationStuff\testing - Copy>set DEST=E:\ImageAutomationStuff\testing - Copy\00FoundImages

E:\ImageAutomationStuff\testing - Copy>pause
Press any key to continue . . .

E:\ImageAutomationStuff\testing - Copy>for /F "delims=" %x in ("E:\ImageAutomationStuff\testing - Copy\imagelist.txt") do (forfiles /p "E:\ImageAutomationStuff\testing - Copy\testimages" /s /m %x* /c "cmd /c move /y @path "E:\ImageAutomationStuff\testing - Copy\00FoundImages\@file""  2>>errors.txt )

E:\ImageAutomationStuff\testing - Copy>(forfiles /p "E:\ImageAutomationStuff\testing - Copy\testimages" /s /m E:\ImageAutomationStuff\testing - Copy\imagelist.txt* /c "cmd /c move /y @path "E:\ImageAutomationStuff\testing - Copy\00FoundImages\@file""  2>>errors.txt )

E:\ImageAutomationStuff\testing - Copy>pause
Press any key to continue . . .

From the research I've done I can see questions of this nature get asked a lot - apologies for adding to the pile but with my limited knowledge I'm afraid I'm simply unable to fix this issue on my own!

like image 205
danmosa Avatar asked Dec 02 '25 10:12

danmosa


1 Answers

To avoid troubles with spaces in paths and file names, use quotes. But you need to be carefull when it comes to forfiles, because all path- and file-name-related @-variables are expanded to values already enclosed within "", so it can easily happen to have values double-quoted.

I would give this a try:

md "%cd%\00FoundImages"

set "LIST=%cd%\imagelist.txt"
set "FILESPATH=%cd%\testimages"
set "DEST=%cd%\00FoundImages"

for /f "usebackq delims=" %%x in ("%LIST%") do (
    forfiles /p "%FILESPATH%" /s /m "%%x*" /c "cmd /c if @isdir==FALSE cd /d 0x22%DEST%0x22 & move /y @path @file" 2>>errors.txt
)

What I did:

  • improved syntax of set so that the entire assignment expression is enclosed within ""; this avoids trouble with some special characters, but the quotes are not part of the variable values;
  • quoted %LIST%, so I had to add the usebackq option to for /f, because it would treat the value of variable %LIST% as a literal string rather than a file otherwise;
  • the path after /p as well as the pattern after /m of forfiles are enclosed in parentheses; here it is very important that the path after /p is not terminated by a \, because this would be interpreted as an escape character by forfiles to escape the closing quote "; if you cannot assure that %FILESPATH% fulfils that criterion, write /p "%FILESPATH%\." instead; edit: the last statement is wrong!
  • the @path variable expands to an already quoted string, so everything is fine with it;
  • the tricky part is the destination file %DEST%\@file from your original code, because @file expands to an already quoted string, so the result would be something like \...\00FoundImages\"FoundImage 1.png", for instance; so quoting the entire expression like 0x22%DEST%\@file0x221 would lead to "\...\00FoundImages\"FoundImage 1.png"", hence the file name FoundImage 1.png appears unquoted to the command interpreter; using 0x22%DEST%0x22\@file1 would lead to "\...\00FoundImages"\"FoundImage 1.png", which does not harm many commands, but could impact some, and it is simply not beautiful; to overcome this, simply change to the directory %DEST% first by cd /d 0x22%DEST%0x221, so it is stated alone, then use @file only for the move command line;
  • finally, forfiles returns both matching files and directories, so to ensure that there are only files processed, I implemented an if query;

1)... the 0x22 expression stands for a " character in forfiles /C command expressions and represents its character code in hexadecimal notation; I prefer this way of specifying quotes, because the other option, \", might impact the command line interpreter as it does not support the backslash-escaping and therefore it recognises the quotation mark, which might alter its behaviour unintentionally.

like image 180
aschipfl Avatar answered Dec 05 '25 01:12

aschipfl