Batch script not working due to spaces in file paths - batch-file

So I'm new to batch scripting and scripting in general. I recently made a script where files from a source directory will be organised according to its 'last modified date' into a destination directory.
Here's an extract of the script:
#echo off
Setlocal EnableDelayedExpansion
echo Organise files
set /P _a="Enter source directory:"
set /P _b="Enter destination directory:"
set /P _c="Organise files by dd-mm-yyyy format (last modified)? y or n?"
if %_c% == y goto:ddmmyy
:ddmmyy
for %%A in ("%_a%\*") do echo %%~tA %%~snA %%~xA >> "%_a%\testtoken.txt"
for /F "tokens=1-7* delims=./ " %%B in (%_a%\testtoken.txt) do (
robocopy "%_a%" "%_b%\%%B-%%C-%%D" "%%G.%%H" /xf testtoken.txt /mov
)
The script works just fine unless I use directories with spaces in their names (eg. C:\Program Files instead of C:\Program). I also had the same issue with spaces in file names until I used 8.3 notation, however using 8.3 notation results in other issues like robocopy not copying files with fairly similar names (eg. TUTORI~2, TUTORI~1, TUTORI~3 etc.)
Here's the format of testtoken.txt:
02/11/2014 06:02 PM SPSETU~1 .exe
16/11/2014 09:19 AM TUTORI~4 .docx
14/11/2014 12:00 PM TUTORI~2 .docx
16/11/2014 09:19 AM TUTORI~3 .docx
14/11/2014 12:00 PM TUTORI~1 .docx
16/11/2014 09:19 AM TU44E0~1 .docx
16/11/2014 09:19 AM TU05DE~1 .docx
16/11/2014 10:59 PM UNIFYI~1 .pdf
09/11/2014 07:55 PM ZOROAS~1 .jpg
So is there a way to make the script disregard the spaces? Why won't robocopy copy files with fairly similar names in 8.3 notation and is there any way to force robocopy to actually copy the files?

#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir"
for %%A in ("%sourcedir%\*") do echo %%~tA %%~fA>> "%sourcedir%\testtoken.txt"
for /F "USEBACKQ tokens=1,2,3,5* delims=/ " %%B in ("%sourcedir%\testtoken.txt") do IF /i "%%~nxF" neq "testtoken.txt" (
REM method 1 - robocopy
echo(robocopy "%%~dpF." "%destdir%\%%D-%%C-%%B" "%%F" /mov
REM method 2 - XCOPY
ECHO(XCOPY "%%F" "%destdir%\%%D-%%C-%%B\"
REM method 3 - MD and move
ECHO(MD "%destdir%\%%D-%%C-%%B\"
ECHO(MOVE "%%F" "%destdir%\%%D-%%C-%%B\"
ECHO ==================================
)
GOTO :EOF
You would need to change the setting of sourcedir and destdir to suit your circumstances. I've changed the variable names to more descriptive names.
Note: I've reversed the date to yyyy-mm-dd format which sorts more easily than dd-mm-yyyy format. Simply reverse the token order %%D-%%C-%%B if you insist on dd-mm-yyyy.
I prefer method 2 or 3 over robocopy so I'm not particularly familiar with robocopy syntax. YMMV.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
The required XCOPY commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(XCOPY to XCOPY to actually copy the files.
The required ROBOCOPY commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(ROBOCOPY to ROBOCOPY to actually copy the files.
Obviously, choose one of the three methods.
Note that your exclusion of testtoken.txt would likely be ineffective since your code targeted short filenames and testtoken.txt is the long filename. I'm not sure that robocopy would do the exception, but the if gate I've implemented certainly will.
Since my date format and your differ, I used your data to test the procedure on my system. Obviously, it seemed to work else I'd not bother posting it.
The token-controls in the for...%%B statement re designed to allot day, month and year to %%B,C,D, skip token 4 (time) and %%E is allotted "AM/PM) - used as a dummy. %%F gets the rest of the line, which should be the full filename generated by the %%~fA into the listing file.
Edited code to remove terminal space being added to testtoken.txt lines in response to OP comment.

You probably should change the format of your file a bit. Instead of making the extension the last column just use the filename there. You then know that the last token extends to the rest of the line, including any spaces:
for /f "tokens=1-5* delims=/ " %A in ("09/11/2014 07:55 PM Test file with spaces.jpg") do (echo %F)
As for quoting any paths with spaces I don't spot any glaring issues right now. for ... in (%_a%\testtoken.txt) may be a problem, which can be solved by using
for /f "usebackq <other options>" ... in ("%_a%\testtoken.txt") do ...
at least if the documentation can be trusted:
usebackq - specifies that the new semantics are in force,
where a back quoted string is executed as a
command and a single quoted string is a
literal string command and allows the use of
double quotes to quote file names in
file-set.

Related

How to delete long name folders under subfolder using batch file

I am trying to delete the sub folders using batch file.
I have folder
c:\users\mis
c:\users\mis\A\Third Party Log
c:\users\mis\B\Third Party Log
c:\users\mis\C\Third Party
c:\users\mis\D\Third Party
Etc.
I want to delete all "Third Party Log" folder and I tire as below:
#echo off
Set MainFolder=C:\users\mis
FOR /D %%D IN ("%MainFolder%\*") DO CALL :RENAME %%D
pause & exit
REM -------------------------------------------------------
:RENAME
Set CRITERIA=Third Party Log
FOR /D %%R IN ("%1\%CRITERIA%") do #if exist "%%R" rd /s /q "%%R"
REM -------------------------------------------------------**
If the folder is only Third it is deleting. Please help me deleting the fodlers.
Thanks,
Htet
#ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "targetdir=u:\your files"
SET "selectname=Third Party Log"
FOR /f "delims=" %%a IN (
'dir /b /s /ad "%targetdir%\%selectname%" '
) DO (
IF /i "%%~nxa"=="%selectname%" ECHO RD /S /Q "%%a"
)
Simply perform a dir /b /ad to list all of the directorynames matching the mask %selectname% and remove all of the hits.
Note that the for loop could be condensed to a single line, but I've split it over a number for clarity.
Naturally, the variables could be substituted by their literal values as well, if desired. This would reduce the entire batch to a single line which could be entered directly at the prompt in which case each %% needs to be reduced to a single %.
Posted code simply echoes the proposed instruction to allow for verification. I the displayed instructions are correct, remove the echo keyword to actually destroy the unwanted directories.
Finally, in your original code, rename is not a good name for the subroutine for two reasons : First, it is misleading as no renaming is being performed and second, ren is an alias of rename - and it's not a good idea to have a routine or variable or label that is itself a command-name.
I can sadly not write code and test it rn but:
You could use the dir command into a temp file.
Use findstr(or find i m not shure) and look for third party log.
Write this back to the temp file.
Now you should be left with a file filled with the paths of the folders you want gone.
Read the file line by line and delete and use this line string to provide the path for the del command.
To change the name as in the sample code you could take the line string and use it as the path to the ren command.
This is most likely a very bad approach but it should work.

Batch: Preserve (creation-)date on copying files to another (flatten) folder structure, incl. renaming files to avoid doublettes

this is my first question, so I apologize beforehand if I write not as you are used to...
fact:
I've a deep folder structure with tons of files (images,videos and so on) so I want to copy that files to a flat structure for a better overview.
I want to keep (at least) the original date attributes: creation-date and last-modified date.
Problem 1) there are files with same name 00001.jpg in different folders which I want to have in same folder, so I want to add creation date/time to filename on copy process.
00001.jpg becomes 2015-11-17_11-23-35_00001.jpg
So far so good. Or not good...
Copy and XCopy doesn't give me an option to do that, without loosing at least the creation date information (I didn`t find a solution with both).
Now I try to copy the files (file by file) with robocopy to new folder and use ren on the copied file to "pre-set" the date/time information before the filename.
Here is a simple test.bat example:
setlocal enableextensions enabledelayedexpansion
robocopy . ./zzz original.txt /copy:DATSO
pause
rem :: formatted creation date of original file will be here, in real life
set "myDate=2015-11-17-_11-23-35"
rem rename "./zzz/original.txt" "!myDate!_renamed.txt" ::doesnt work: why? relative path??
rem :: this will do what I want - original creation date is kept on copy file
FOR %%A IN (zzz/original.txt) DO REN "%%~fA" "!myDate!_%%~nxA"
[possibly] Problem2) Is there a better way to do this, or could I run into thread problems (asynchronous execution). Could it be, that I try to rename a file before the robocopy has finished the copy process (e.g. for large files)?
Sorry I'm a totally batch newbie (also as poster in SO ;).
ThanX in advance for each tip and also for critics on my solution approach. Maybe I have the horse-blinkers on my head and dont see the easy solution?!
[edit: formatting of post]
[edit: content of post -> date/time in front of filename for better sorting]
It is possible to use command DIR to get recursive listed all files in the specified folder and its subfolders with creation date/time.
The format of the date/time depends on Windows Region and Language settings.
Example output for F:\Temp\Test on my machine with English Windows 7 and region is configured to Germany on running the command line dir F:\Temp\Test\* /A-D /S /TC:
Volume in drive F is TEMP
Volume Serial Number is 1911-26A4
Directory of F:\Temp\Test
25.09.2017 17:26 465.950 SimpleFile.ccl
1 File(s) 465.950 bytes
Directory of F:\Temp\Test\Test Folder
25.09.2017 17:26 360.546 Test File.tmp
1 File(s) 360.546 bytes
Total Files Listed:
2 File(s) 826.496 bytes
0 Dir(s) 58.279.460.864 bytes free
This output is filtered with findstr /R /C:"^ Directory of" /C:"^[0123][0123456789]" to get only lines starting with  Directory of (note the space at begin) or with a number in range 00 to 39.
Directory of F:\Temp\Test
25.09.2017 17:26 465.950 SimpleFile.ccl
Directory of F:\Temp\Test\Test Folder
25.09.2017 17:26 360.546 Test File.tmp
And this filtered output is processed by FOR loop and the commands executed by FOR.
#echo off
for /F "tokens=1-2*" %%A in ('dir "F:\Temp\Test\*" /A-D /S /TC ^| %SystemRoot%\System32\findstr.exe /R /C:"^ Directory of" /C:"^[0123][0123456789]" 2^>nul') do (
if "%%A %%B" == "Directory of" (
set "FilePath=%%C"
) else (
set "CreationDate=%%A"
set "CreationTime=%%B"
for /F "tokens=1*" %%I in ("%%C") do set "FileName=%%J"
call :RenameFile
)
)
goto :EOF
:RenameFile
set "NewName=%CreationDate:~-4%-%CreationDate:~3,2%-%CreationDate:~0,2%_%CreationTime:~0,2%-%CreationTime:~3,2%_%FileName%"
ren "%FilePath%\%FileName%" "%NewName%"
goto :EOF
It would be advisable to put command echo before command ren in last but one line to first verify the expected new file names.
ren "F:\Temp\Test\SimpleFile.ccl" "2017-09-25_17-26_SimpleFile.ccl"
ren "F:\Temp\Test\Test Folder\Test File.tmp" "2017-09-25_17-26_Test File.tmp"
Note: The batch file must be in a folder not processed by this batch file as otherwise the batch file itself would be renamed while running which breaks the processing of the batch file.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
for /?
goto /?
if /?
ren /?
set /?
By the way: WinRAR can add files into a RAR archive with creation and last access time in addition to last modification time and extract the files to a different directory with restoring also creation and last access time using SetFileTime function of Windows kernel.
currently I use Locale independent date. I use tokens from that for currrent date/time.
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
rem :: format it to YYYY-MM-DD_hh-mm-ss
set myDateTime=%datetime:~0,4%-%datetime:~4,2%-%datetime:~6,2%_%datetime:~8,2%-%datetime:~10,2%-%datetime:~12,2%
Thats not the problem.
To clarify:
The listing is also not the problem.
I loop throw all related files without a prob (except batch files and output dir and its sub tree ;).
My problem is:
I call robocopy for each file, then I rename the file to save the original creation date. My fear is that it makes problems (Multi-Threading?) for large files and for the number of calls (many thousend times)
Is batch execution really serialized? Is the process waiting for robocopy, that it has finished, before I try to rename file. Could I run into dead-locks for vry large files? I'll test it with some fake gigabyte-files.
Your suggestion, to use winrar sounds interesting.
If I could add all that files to a big archive (with structure) and at the end extract it to target dir... I'll try it ;)
If it doesn't work I will program it in java!
There I know what to do, thats my playground :D
I thought it would be easy to write a simple batch file, to do this for me, but it seems it's not as easy as I thought!
ThanX anyway

batch file : overwrite one file to several existing files

I am trying to make simple security program for my company.
We usually make a lot of doc or ppt(x) files and
for some reason, we need to make them disable as soon as possible.
So we usually deleted the all of files but It took so long.
So I thought I can do that by overwritting the files.
If I have a empty doc or ppt files then overwite all of doc, ppt files in working drive each, then it will be faster and much safer than just deleting.
So I tried to use xcopy
Assuming empty.doc is just empty doc file and
xcopy /s /y c:\users\mycom\empty.doc c:\*.doc
But it said cannot perform cyclic copy
I need you guys help
and I am glad to hear suggestion.
Thanks.
This is an old, old batch file that I employed for such an endeavour:
:: dispose.bat
#echo off
:: Check parameter(s), stripping quotes
set yp1=%1
set ydf=
:insistp1
if defined yp1 set yp1=%yp1:"=%
if not defined yp1 for %%i in (echo goto) do %%i error - filename required
if not exist "%yp1%" for %%i in (echo goto) do %%i error - file not found
for %%i in ("%yp1%") do (set yfr=%%i&call :zapit %%~di) 2>nul
:error
:: Clean up variables used
if defined ydf echo Warning! dispose failed!!
for %%i in (yp1 yfr yrn ydf) do set %%i=
goto :eof
:zapit
set yfr=%yfr:"=%
IF /i %1 == u: DEL "%yfr%" &goto :eof
if not exist %1\delete\. md %1\delete
(set yrn=)
:rndloop
set yrn=%yrn%%random%
if exist %1\delete\%yrn% goto rndloop
if not exist "%yfr%" set ydf=Y&goto :eof
move "%yfr%" %1\delete\%yrn%>nul
goto :eof
:: dispose.bat ends
Noting that u: is a RAMDRIVE on my system, hence mere deletion is all that is required.
The purpose is not to actually delete the files, but to move them to a directory named ?:\delete and provide them with a random name in that directory.
Since the file is simply MOVEd it is quite fast, which addresses your time consideration.
An issue for me is the idea of copying a file over all of the files you target. If the file that you copy is shorter than the other files, some data wilstill be available to be recovered. Regardless, it will alwats be slower than simply deleting the files (which you say you are currently doing.)
This scheme simply accumulates the files-to-be-deleted in a known directory on the same drive (so they will simply be moved, not copied.)
Once they are in your \delete directory, you can let a utility like ccleaner or recuva loose on that single directory in the background and it will overwrite the files a specified number of times.
Here's a simpler method. Be careful.
At the command line:
for /r c:\ %A in (*.doc? *.ppt?) do echo. > %A
In a batch file:
for /r c:\ %%A in (*.doc? *.ppt?) do echo. > %%A
EDIT:
To replace with a file, see the example below. Replace the example's d:\path\file.ext with your intended file. Note that the previous option will work much faster with a similar result.
At the command line:
for /r c:\ %A in (*.doc? *.ppt?) do copy d:\path\file.ext > %A
In a batch file:
for /r c:\ %%A in (*.doc? *.ppt?) do copy d:\path\file.ext > %%A
Either way, as noted in Magoo's answer, larger files will still have recoverable data on the drive. You stated in a comment:
But if I overwrite the original files, then they cannot guess what it
was unless they got bak files
This isn't accurate. Forensic tools can retrieve the partial data that wasn't overwritten with new content.

SIMPLE :: XCOPY NOT PRODUCING OUTPUT IN .bat

I'm a mortgage loan guy in Seattle, WA and I frequently set up a folder hierarchy into which I save a client's documents and as they come in to me. I've been creating these manually for years and I'd like to save the 3 to 4 minutes it takes to set these up by using a batch file.
So... I have a default set of folders, some of which contain a couple of small Adobe PDFs. What I'd like to do (and cannot make happen) is to run a batch file that would facilitate some custom remarks or input from me during the batch so that with a click and a couple of keystrokes, I have an organized folder setup for a new client within seconds rather than minutes.
I've written the following but it isn't producing any output folders or files.
______not sure character terms show correctly - see linked images below for actual______
#echo off
::Ask
echo Your Source Path:
set INPUT1=
set /P INPUT1=Type input:
echo Your Destination Path:
set INPUT2=
set /P INPUT2=Type input:
C:\WINDOWS\system32\xcopy.exe /e /v %INPUT1% %INPUT2%
My responses were:
to the first prompt "E:\DV8333 MY DOCUMENTS\002 ATLAS\ATLAS RESOURCES\000NEWCLIENTFOLDER2014"
to the second prompt "E:\DV8333 MY DOCUMENTS\001 CLIENTS\"
I have verified that xcopy.exe is in fact located as indicated above.
I'm on XP SP3
My actual paths and .bat file are shown in the linked image for clarity.
http://www.avidrecording.com/images/01.png
Thanks in advance, much appreciated.
#Rem save this as a .bat file and run
#echo off
set /P source=Enter Source Folder:
echo %Source%
set /P destination=Enter Destination Folder:
echo %destination%
xcopy %source% %destination% /v /i /e
Fundamentally, your problem appears to be that the xcopy command can't figure out which of the data it is receiving is parameters and which switches and which superfluous because you have spaces in your directorynames.
Fortunately, the cure is simple. "quote your parameters"
C:\WINDOWS\system32\xcopy.exe /e /v "%INPUT1%" "%INPUT2%"
Now - the path to xcopy.exe is probably superfluous - as is the extension, so
xcopy /e /v "%INPUT1%" "%INPUT2%"
is more than likely all you'd need.
(I'd caution to experiment with a throw-away destination until you've perfected your method. I use a RAMDISK...)
Also, if you're copying a template, then there's no apparent reason for all the folderol about inputting Input1. If you have more than one template set, set up a separate batch and shortcut for each one with a fixed template path, eg
xcopy /e /v "E:\DV8333 MY DOCUMENTS\002 ATLAS\ATLAS RESOURCES\000NEWCLIENTFOLDER2014" "%INPUT2%"
Note the use of quotes to defeat the evil spaces.
Next, your destination could be built, but may contain spaces. For instance, you may have a client to which you wish to refer as "037 - Fred Nurk". Now it's a pain to have to type in the full path, so make it easy. Just type in the 037 - Fred Nurk part and let batch fill in the rest.
xcopy /e /v "E:\DV8333 MY DOCUMENTS\002 ATLAS\ATLAS RESOURCES\000NEWCLIENTFOLDER2014" "E:\DV8333 MY DOCUMENTS\001 CLIENTS"\"%input2%"\
Note that this will append the input as a directory under E...001 clients. Note that the strings are concatenated and the double-quotes are there solely to tell batch "here be a string that may contain spaces."
If this works, and there's no reason why it wouldn't (does for me...) then all you'd need to do is enter the client details and the template would be copied to a new directory. Now actually playing around with the data in the files that are copied so that they are customised - well, at the price, that would be worthy of another question...
#echo off
echo Backing up file
set /P source=Enter source folder:
set /P destination=Enter Destination folder:
set /P Folder name=Enter Folder name
set xcopy=xcopy // Set the switches as per your need
%xcopy% %source% %destination%
pause

Batch file for opening most recent Malwarebytes' Anti-Malware Log

In Vista, I want to run a batch script to open the most recent file, based either on last modified date, or the date in the filename. It's for Malwarebytes' Anti-Malware logs in the %username&/appdata/roaming/Malwarebytes/Malwarebytes' Anti-Malware/Logs folder.
Log files are in this format here
mbam-log-2009-03-21 (00-20-21).txt
mbam-log-2009-03-21 (09-42-40).txt
mbam-log-2009-03-21 (11-02-43).txt
mbam-log-2009-03-21 (11-12-01).txt
mbam-log-2009-03-21 (12-01-42).txt
mbam-log-2009-03-21 (12-04-49).txt
mbam-log-2009-03-21 (14-01-41).txt
So its 24-hr format. I read on another page on here, and got this script here..
#echo off
dir *.txt /b /on > systext.bak
FOR /F %%i in (systext.bak) do set sysRunCommand=%%i
call %sysRunCommand%
del systext.bak /y
but it doesn't like the space in the filename.. always get an error.
Anyone have any ideas?
You actually have three problems in the script. The first is that %%i is likely to be set to the words with a filename- this can be fixed by using "delims=" in the for statement.
The second is that you need to quote spacey filenames in you call statement.
The third is that I'm not aware of a /y option for del, perhaps you mean del /f.
Amyway, give this one a shot:
#echo off
dir *.txt /b /on > systext.bak
FOR /F "delims=" %%i in (systext.bak) do set sysRunCommand=%%i
call "%sysRunCommand%"
del /f systext.bak
The following one-line batch file will open the most recently modified file:
for /f "usebackq delims=" %%i in (`dir /b /o-d`) do #start "%%i"&goto :eof
Using dir strikes me as much simpler than trying to dissect the date in the filename. You can also order by filename (as the date format is somewhat ISO-8601-ish is sorts well).
The goto :eof is just there to make sure only the most recent file will be opened and not all in order of date/time.
As for your space problem, surrounding the file name with quotes usually should fix that, but sometimes it's a little difficult to know where they have to be. Also, for by default tokenizes its input at spaces, that's why I included delims= in there which essentially says »Put everything into the variable and don't do any tokenizing«.
I know this is an old post, and I don't mean to revive it - I have an alternative approach to the question.
Malwarebytes' Anti-Malware includes the ability to reroute the log file to an alternate location (folder or specified file).
You can run "%programfiles%\malwarebytes' anti-malware\mbam.exe" /logtofile c:\logs\bob-mbam-log.txt. /logtofolder also works. (the above command assumes you're using a 32 bit system.)
When specifying a file, mbam appends instead of overwriting. So, that may make the log a little more difficult to parse.

Resources