Batch file with spaces in variables/filenames in text file - variables

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:
A filename in the text file contains a space
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!

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.

for /f "delims=" %%x in ("%LIST%") do (forfiles /p "%FILESPATH%" /s /m %%x* /c "cmd /c move /y #path \"%DEST%\#file\"" 2>>errors.txt)
Quotes within the quotes that is the command forfiles runs have to be escaped with a backslash. But not other quotes like starting folder.

Although I do not understand why do you nest FORFILES loop in another (FOR /F) loop, next code snippet should do the trick:
for /f "usebackq delims=" %%x in ("%LIST%") do (
forfiles /p "%FILESPATH%" /s /m "%%~nxx" /c "cmd /c ECHO move /y #path \"%DEST%\"\#file" 2>>errors.txt
)
Note:
double quotes in "%FILESPATH%" and "%%~nxx";
escaped double quotes in \"%DEST%\";
plain #path and #file as those FORFILES command variables are already double quoted;
maybe that you need to reformulate /m "%%~nxx" file mask (I use %%~nxx file name and extension because I don't know %LIST% file content and suppose a list of fully qualified file names);
important: move command is merely displayed for debugging purposes; remove preceding ECHO to make it operational, no sooner than debugged.

Related

DOS Batch File Variable Modifier Returns Blank

I have a DOS batch file that will create a report listing files contained within a folder tree. The code below produces the desired output for over 115,000 files. However, 13 records are produced with blank date/time and file size. When I manually execute the DIR command (without the /b option), the desired file information is presented. Can this be corrected without adding considerable workaround code?
FOR /f "tokens=*" %%A IN ('DIR "<Path>" /a:-d /b /s') DO (
ECHO %%~tA %%~zA %%~dpA %%~nA %%~xA >> test.txt
)
(FOR /f "tokens=*" %%A IN ('DIR "<Path>" /a:-d /b /s') DO (
if exists "%%~A" ECHO %%~tA %%~zA %%~dpA %%~nA %%~xA
)) >> test.txt
The main reason for not obtaining a date/filesize is that the file can not be found.
How does your code work?
The for /f starts a separate cmd instance that runs the dir command.
When all the data has been retrieved and loaded into memory (that is, the cmd/dir command finished), then the for will start to iterate over the retrieved lines. Some time have passed between the information retrieval and the information processing.
In this time, "maybe" the problematic files have been moved/deleted/renamed and they can no be accessed to retrieve their properties. So, first check if the file still exists
The aditional parenthesis and redirection change are to avoid having to open the target file for each echo operation. This way, the file is opened at the start of the for command and closed at the end.

Removing everything after specific character in batch

SETLOCAL ENABLEDELAYEDEXPANSION
set "s=DIR D:\MyFolder /S /Q ^|FIND /i "Owner" ^|findstr /m /i "\.mkv$""
for /f "Tokens=5,6*" %%a in ('%s%') do (
SET _endbit=%%aa:*STRING=%
CALL SET _result=%%aa:%_endbit%=%%
>>%tmp%\list.txt echo %_result% %%b %%c
)
wscript "C:\my.vbs"
I am listing my files that owned by Owner and has extension mkv from MyFolder. I want to remove everything after specific character/word. I wrote that code. But It seems to be not working.
First of all, is it possible to do that? If so what is wrong with my code?
You came up with the same solution I was going to post on your other question - but I think you're missing some things. The line ending STRING=% is missing another % at the end, and the line %%aa:%_endbit%=%%, I'm not sure it can work directly on the variable from the for loop, and you need to store it in another variable first, and you probably need some expansions using ! characters too.
Here's what I had that seems to work, just as a test in a folder with three files in it, removing the end of the filename using E0 as the cutoff string:
SETLOCAL ENABLEDELAYEDEXPANSION
#echo off
for %%f in (*.mkv) do (
set FULLNAME=%%f
set ENDTEXT=!FULLNAME:*E0=!
call set TRIMMEDNAME=%%FULLNAME:!ENDTEXT!=%%
echo !TRIMMEDNAME!
)

Set file name with * as variable batch script

I have a batch file that i'm having issues with. I need to find the name of a file, then set it to a variable. Then I will use this to pass it onto a vbs script to further look into the file. The name of the file is logfile_date_time.log but the time varies depending on what time is starts. The point of the batch file is to find out the last modified date of this file.
set fordate=%date:~4,2%%date:~7,2%%date:~10,4%
set filename=c:\logfile_%fordate%_*.log
if exist %filename% (goto exist) else (goto noexist)
:exist
vbsscript.vbs /file:%filename%
goto end
:noexist
file doesn't exist code blah blah
:end
pause
I had to modify the names of folders and remove some code for security purposes since this is for work.
Any help appreciated. Thanks!
not tested:
set "last_modified="
for /f "delims=" %%f in ('dir /a-d /tw /o-d /b^| findstr /r /i /c:"logfile_[0-9][0-9]*_.log"') do (
do set "last_modified=%%~dpfnxf"
goto :break_loop
)
:break_loop
if defined last_modified echo file %last_modified% exist ...
The problem with your code is that it doesn't expand the wildcard character (*), and your VBScript probably doesn't handle wildcards in filenames by itself (the FileSystemObject methods for instance don't). If the file you want to process is the only one matching your pattern, you could do something like this:
#echo off
setlocal
set "fordate=%date:~4,2%%date:~7,2%%date:~10,4%"
pushd C:\
for %%f in (logfile_%fordate%_*.log) do vbsscript.vbs /file:"%%~ff"
popd

Batch file that takes the file for the previous day only and copy it elsewhere

OK, so I've done a bit of research but have hit a bit of a hurdle here. What I'm trying to do is, (using event scheduler), run a batch daily, that takes the "ips*.csv file (* is just the date format) for the previous day only and copy it elsewhere, for my test I used "destiny ips" folder. I think I've messed up on the last hurdle.
FORFILES /P "C:\Users\J\Desktop\ips bat\source ips" /M "ips*.csv" /C "cmd /c XCOPY C:\Users \J\Desktop\ips bat\source ips folder C:\Users\J\Desktop\ips bat\destiny ips" /D -1
Thanks for any help in advance.
Jonny
for /f "delims=" %%i in (
'dir /b /o:-d "c:\users...etc...ips*.csv" ' ) do (
xcopy /d "%%i" "C:\Users\J\Desktop\ips bat\destiny ips\" &goto :eof
)
should do this very simply (in fact, it can all be on the one line) It will copy only the very latest version of the file to the destination - and then only if the destination file does not already exist (or is different from the version in source.) This should take care of any non-operational days like weekends or public holidays.
If the destination keeps ALL of the history (ie. there may be ips*.csv files in the destination that are not in the source, but not vice-versa (other than the new one for "yesterday") ) then simply
xcopy /d "c:\users...etc...ips*.csv" "C:\Users\J\Desktop\ips bat\destiny ips\"
will suffice.
The critical points are:
the destination ends in "\" to say 'this is a directory name'
the /d which will tell XCOPY to copy only new or updated files.
(It stands to reason that ...etc... is the remainder of the pathname required - DOS is not psychic)
To include special characters in the command line, use the hex code for the character in 0xHH format (ex. 0x09 is theTAB character, 0x22 is the double quote " character.) so "C:\Program Files\" becomes ^0x22C:\Program^ Files\^0x22
FORFILES
/P "C:\Users\J\Desktop\ips bat\source ips"
/M "ips*.csv"
/C "cmd /c
XCOPY ^0x22C:\Users\J\Desktop\ips^ bat\source ips folder^0x22
^0x22C:\Users\J\Desktop\ips^ bat\destiny ips^0x22"
/D -1
I have separated it onto multi-lines for readability, but you will want to put that back into one line.
This looks suspect-:
XCOPY C:\Users \J\Desktop\ips bat\source
Is it correct?

DOS Batch file - Copy file based on filename elements

I need to sort alot of files based on their filename. I would like to use a batch file to do it. I do know what I want but I am not sure of the correct syntax.
Example of filenames I work with: (They are all in the same directory originally)
2012_W34_Sales_Store001.pdf
2012_W34_Sales_Store002.pdf
2012_W34_Sales_Store003.pdf
2012_November_Sales_Store001.pdf
2012_November_Sales_Store002.pdf
2012_November_Sales_Store003.pdf
I would like to extract the information that are located between the "_" signs and put them into a different variable each time. The lenght of the informations contained between the _ signs will be different everytime.
Example:
var1="2012"
var2="W34" (or November)
var3="Sales"
var4="001"
If I am able to do this, I could then copy the files to the appropriate directory using
move %var1%_%var2%_%var3%_%var4%.pdf z:\%var3%\%var4%\%var1%\%var2%
It would need to loop because I have Store001 to Store050.
Also, there are not only Sales report, many others are available.
I hope I am clear.
Please help me realize this batchfile!
This script will make sure that it only attempts to move files that meet the pattern part1_part2_part3_part4.pdf
#echo off
for /f "eol=_ delims=" %%F in (
'dir /b *^|findstr /ix "[^_]*_[^_]*_[^_]*_[^_]*[.]pdf'
) do for /f "eol=_ tokens=1-4 delims=_." %%A in ("%%F") do (
move "%%F" "z:\%%C\%%D\%%A\%%B"
)
If needed, you could add md "z:\%%C\%%D\%%A\%%B" 2>nul before the MOVE in case the folders might not exist yet.
This script will move your files based on the values between the underscore to a like wise constructed path.
for %%f in (*.pdf) do call :handlefile %%f
:handlefile
set pad=z:
for /f "delims= tokens=1,* " %%a in ("%1") do call :step %%a %%b
rem this MOVES the file, maybe use echo first for testing
move "%fn%" "%pad%"
exit /B
:step
if !%2!==!! EXIT /B
set pad=%pad%\%1
for /f "delims=_ tokens=1,* " %%a in ("%2") do call :step %%a %%b
EXIT /B