I am a bit new to DOS batch files, and I am having a hard time wrapping my head around ways to solve my problem.
What I need to do: I have a large, nested source folder structure, let's say it lives here:
C:\dir1\dir2\dir3\file.txt
And I have a mirrored destination structure, although a portion of the root is different:
x:\dirA\dirB\dir1\dir2\dir3\file.txt
My current batch copies all files in the source structure to a destination folder, keeping the folder structure, which is what I want.
The problem:
The intention of my script is to drag a folder from the source structure above onto the bat file, and have it copy the files to the destination. What I want to do, is to allow the user to drag a folder from source dir, let's say /dir2 and all of its subfolders/files onto the batch file, and have it copy the contents over to the SAME spot in the destination structure...
So in this example, the batch file should copy everything in and below:
C:\dir1\dir2\
into
x:\dirA\dirB\dir1\dir2\.
Fortunately (I think) my destination folder structure won't be changing, although the source might be in a different location on each machine. So, if there is a clever way to detect where in the source tree I am, and then replace a portion of the destination path... ???
Here's my simple script so far:
#echo off
set /p FILETYPE="What file type do you want to copy? (For example use txt, max, f3d, or * to copy all files.)"
xcopy %1\*.%FILETYPE% c:\output /s
pause
Thanks so much for any help you guys can give! :)
Ken
UPDATE: Adding updated code sample here because I cannot get my comment to format or allow me enough chars. The stuff below may not be completely correct, I am just trying my best to be clear. I have figured out more since I posted this, basically I need to figur out how (or if possible) to use a string after delims, it only seems to check for each character...
#echo off
rem user drag folder onto .bat (left click, move folder using mouse onto .bat icon)
rem %1 in ex is C:\random_folder\another_folder\proj1\area1\scene23
set destRoot=X:\companyname\allprojects\proj1
set rootDir=proj1
rem assume %1 is folder dragged onto .bat file
for /f "tokens=2* delims=<foldername???>" %%a in ("%1") do (
set part1=%%a
set chunk=%%b
)
set finalDest=destRoot+chunk
xcopy %1\* %finalDest% /E /EXCLUDE:exclusions.txt
pause
I am hoping to create this: "X:\companyname\allprojects\proj1\area1\scene23"
Is the x:\dirA\dirB\ part of the destination folder known? If so, the solution is easy:
#echo off
rem Get current directory, ie: "C:\dir1\dir2"
set curDir=%cd%
rem Eliminate the disk drive, ie: "\dir1\dir2"
set curDir=%curDir:~2%
rem Copy to right destination, ie: "x:\dirA\dirB\dir1\dir2"
xcopy *.%FILETYPE% "x:\dirA\dirB%curDir%"
I hope it helps...
EDIT: Clarification to new comments requested
Ok. There is a source folder and a destination folder. The program above assume that the current folder for the program is source folder, but this may be provided as a parameter if needed. The program achieve the following copy processes:
From source folder -> To destination folder
C:\dir1\dir2 -> x:\dirA\dirB\dir1\dir2
C:\dir1\dir2\dir3 -> x:\dirA\dirB\dir1\dir2\dir3
C:\dir1\dir2\dir3\what\ever -> x:\dirA\dirB\dir1\dir2\dir3\what\ever
C:\XYZ -> x:\dirA\dirB\XYZ
If this is not what you want then explain it as concise as possible (use "files", "folder", "part of name" terms, not "I like", "what if", etc), and include three or four examples.
Also, note that for me drag a folder mean a drag&drop operation taking the source folder pressing the right button of the mouse and leave it over the icon of the Batch file. If you are NOT refering to this operation, please do NOT use "drag" term; use "copy" instead (please also clarify this point).
EDIT: Solution to new requirements
OK! You have not explained correctly before your last example! This problem is just about matching two names: last part of first name must match the same part in second name, and combine first name and the rest of second name. Right?:
First name: X:\companyname\allprojects\proj1
Second name: C:\random_folder\another_folder\proj1\area1\scene23
Result: X:\companyname\allprojects\proj1\area1\scene23
This is the solution:
#echo off
setlocal EnableDelayedExpansion
rem user drag folder onto .bat (left click, move folder using mouse onto .bat icon)
rem %1 in ex is C:\random_folder\another_folder\proj1\area1\scene23
set sourceFolder=%~1
set destRoot=X:\companyname\allprojects\proj1
rem Get last part of First name (destRoot) ie: lastPart=proj1
for %%a in (%destRoot%) do set lastPart=%%~Na
rem Delete from beginning of second name until that part, ie: result=\area1\scene23
set result=!sourceFolder:*%lastPart%=!
rem And insert the First name before the rest
set result=%destRoot%%result%
Or, combining the three steps in just one line:
#echo off
rem user drag folder onto .bat (left click, move folder using mouse onto .bat icon)
rem %1 in ex is C:\random_folder\another_folder\proj1\area1\scene23
set sourceFolder=%~1
set destRoot=X:\companyname\allprojects\proj1
for %%a in (%destRoot%) do set result=%destRoot%%sourceFolder:*%%~Na=%
Please note that if destRoot may contain spaces, it must be enclosed in quotes in the FOR command: for %%a in ("%destRoot%") do ...
Got this on another forum, seems to contain the answr I was looking for, sharing here for others:
#echo off
REM YOU NEED THIS
setlocal enabledelayedexpansion
REM If the passed parameter string has any spaces you must quote it
REM we unquote it using ~
set PassedString=%~1
REM May as well do the same with string to split on
set DelimString=%~2
REM The delim string must appear exactly ONCE in the passed string
REM Replace the delim string with a symbol which will not be in the
REM path to be split, in this example I use #
set splitsub=#
call set tempstring=!PassedString:%DelimString%=%splitsub%!
REM Use FOR to split the modified string
for /f "tokens=1* delims=%splitsub%" %%A in ("%tempstring%") do set part1=%%A & set part2=%%B
REM Show that the 2 variables contain the desired substrings
echo Passed = %PassedString%
echo Part1 = %part1%
echo Part2 = %part2%
Example output:
C:\>splitstring.bat "D:\Backup\mystuff\programming\Visual Basic\project\ABCDE\DEF" "project"
Passed = D:\Backup\mystuff\programming\Visual Basic\project\ABCDE\DEF
Part1 = D:\Backup\mystuff\programming\Visual Basic\
Part2 = \ABCDE\DEF
C:\>splitstring.bat "D:\Backup\mystuff\programming\Visual Basic\project\ABCDE\DEF" "mystuff"
Passed = D:\Backup\mystuff\programming\Visual Basic\project\ABCDE\DEF
Part1 = D:\Backup\
Part2 = \programming\Visual Basic\project\ABCDE\DEF
Related
I have a quantity of folders with archive files,
Parent folder with subfolder eg
Graphics:
graphics 01012021/file31241.7z
graphics 01022021/file4231.7z
odds and ends 01032022/filejohnny.7z
etc
each folder contains an archive - various names numbers.
each archives contains various files, pdf's txt files invoices and image files.
Generally the images are .jpg named various names.
What I would like to do is batch attack the parent folder to extract an image file/s from the each archive from each sub directory and leave the image in the subdirectory with the archive where it came from. If the archive has multiple images that's fine, I am not targeting a single particular image.
hopefully ending up with something like
Graphics:
graphics 01012021/
file31241.7z
yellowstone.jpg
flintstone.jpg
graphics 01022021/
file4231.7z
martha.jpg
odds and ends 01032022/
filejohnny.7z
artemis.jpg
French toast.png
I would rather avoid if possible extracting all the files separating the images then having to re archive.
What I tried to discover originally was to batch extract the image files to the directory it belongs to, have the image file renamed to its directory name. I didn't get close with a solution, so I think if possible just extracting the image would be fine and I can use a renaming app to do the other I've found bulk rename utility to be just fine once I got my head around it.
You wouldn't think that over the years you would collect so many archives, like small drops they ended up become an ocean full.
I have tried researching stack and seen a lot of examples of how eg 7zip works but I just cant get my head quite around it.
I am due to retire they tell me 65 is the time for the chicken coop, I've been a pencil pusher and mouse skater most of my life in the gfx industry. I used to know what was in each archive but memory is a little how to say... rusty nowadays, I know all my archives have images in them. My life would be a lot easier in the sunset of it to look at the pictures and not have to rack my brains trying to remember what was in the archive itself.
Cheers and ty in advance from the colonies downunder.
Grumpy
I found the solution to my issue, no coding just using an app I've been using for years. Total Commander.
Solution was simple in the end.
I open up Total Commander, do a search for the archive files I want Alt F7 it will list in the right hand frame all my archives .7z .zip whatever you have.
Then you select "feed to list box" found in the bottom right hand corner. Do a Ctrl A then Alt F9 which gives you some options.
You clear unpack specific files from archive to "make sure its blank" then files to unpack tell it what your looking for in my case .jpg (it can be any specific file).
Untick unpack path names if stored with files
tick or untick overwrite existing files
untick unpack each archive to a separate subdir (name of the archive)
Hit ok.
It will then search and find the file/s you are looking for and unpack them in the directory/subdirectory/etc they are found in.
Job done... No coding just using TC.. marvellous app
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
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 "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "outfile=%destdir%\outfile.txt"
SET "extensions=bat txt"
SET "archives=7z zip"
CALL :prefix sdexts *. %extensions%
CALL :prefix sdarchives *. %archives%
CALL :prefix dexts . %extensions%
CALL :prefix darchives . %archives%
PUSHD "%sourcedir%"
(
FOR /f "delims=" %%b IN ('dir /b /s /a-d %sdexts% %sdarchives%') DO (
SET "unreported=%%b"
FOR %%o IN (%darchives%) DO IF /i "%%~xb"=="%%o" CALL :procarch%%o&SET "unreported="
IF DEFINED unreported ECHO %%~dpb ^| %%~nxb
)
)>"%outfile%"
popd
GOTO :EOF
:: return %1 with each other argument preceded by '%2'
:prefix
SET "$1=%1="
shift
SET "$2=%~1"
:sdl
SHIFT
IF "%1"=="" SET "%$1%"&GOTO :eof
SET "$1=%$1% %$2%%1"
GOTO sdl
:: Process .7z or .zip archives
:procarch.7z
:procarch.zip
SET "skipme=Y"
FOR /f "delims=" %%e IN ('7z L "%unreported%"') DO (
FOR /f "tokens=3delims= " %%o IN ("%%e") DO (
IF "%%o"=="------------" (
IF DEFINED skipme (SET "skipme=") ELSE (SET "skipme=Y")
) ELSE IF NOT DEFINED skipme ECHO "%%o"|FIND "D" >NUL &IF ERRORLEVEL 1 (
SET "filename=%%e"
FOR /f "delims=" %%y IN ("!filename:~53!") DO (
FOR %%c IN (%dexts%) DO IF /i "%%c"=="%%~xy" ECHO %unreported% ^| %%y
)
)
)
)
GOTO :eof
I've a similar requirement, so I spent a bit of time on this...
The first part simply sets the directories to be used.
I used bat and txt as extensions required for testing. Change as desired - just a space-separated list.
Similarly, I chose to examine .zip and .7z archives. Since 7zip can deal with both, the same subroutine can be used to process both. Other archive types may need different processing.
The code uses dir to locate the files of interest, and dir can take a series of arguments, so the routine :prefix converts the list provided as %3+ to a list in variable %1, each term prefixed by the string at %2.
So - sdexts will become *.bat *.txt and dexts .bat .txt for instance.
Next, switch to the source dir to begin the dir to list the required files. For each name returned in %%b (delims= applies the full name, /b basic list (names only), /s processes subdirectories, /a-d suppresses directorynames) - the flag unreported is set to the filename found then the extension of the filename found is compared to the darchives list. If a match is found, execute the procarch%%o routine is executed and the unreported flag set to empty. Then, if unreported has not been cleared, it's not an archive file, so report it.
procarch%%o will be resolved either procarch.7z or procarch.zip and a routine (actually the same routine) is provided.
The :prefix routine sets $1 to %1=, so for example, sdexts=. Then it shifts the parameter list and sets $2 to the prefix to be placed before each term.
Then shift again. If %1 is not empty, append a space and the prefix in $2 and %1 to $1 and repeat.
When %1 becomes empty, execute the command set "%$1%" and finish, so as $1would then contain the stringsdexts= *.bat *.txt, sdexts` would be set to the list required.
The routine :procarch.7z or procarch.zip (the first simply falls through to the second, so the same processing is executed) sets the flag skipme to non-empty as we need to skip 7z's header data.
%%e acquires each line of the 7z L report in turn. %%o is assigned the third token of the 7z report line. This will be junk up to the line with a series of dashes, then the attribute report for the archived items, then another series of dashes, then junk.
So - if %%o is a string of dashes, simply switch skipme to empty/not empty.
If %%o is not dashes, and skipme is not defined then we are between the two lines of dashes (ie. on an archived item line) so we see whether %%o contains D (which would indicate that this is a directory name, which we do not want to report). If it does not, errorlevel will be set non-zero by the find (output of the find is sent to nul=the æther) filename set to the full report line. We're not interested in the first 54 characters, but the remainder contains the archived filename, so see whether the extension of the filename found is one of interest (in dexts) and if it is, report it.
The ( on the line preceding the for ... %%b matches the )>"%outfile%" and sends all output that would otherwise go to the console to the file named.
And the ^| on the echo lines output a literal | character as | is a special character for cmd and needs to be escaped by ^ to be interpreted as a literal.
To answer your question the task is simple involving For loops with recursion, however to be robust the solution will be complex without knowing how those specific long term possibly mixed, 7zip files are subdivided, thus if a 7zip has itself two sub folders with identical named files you will hit error conditions. I have allowed for that using -aou to auto rename if necessary. however I have not added the folder name to each file as that's an extra step.
#echo off & Title Extract Images from 7z files
set "extractor=C:\Program Files (x86)\7-Zip\7z.exe"
set "archives=*.7z"
set "filetypes=*.png *.jpg *.jpeg *.tif *.tiff"
set "startDir=C:\Users\WDAGUtilityAccount\Desktop"
FOR /R "%startdir%" %%I IN (%archives%) DO ( "%extractor%" e "%%I" -o"%%~dpI" %filetypes% -aou)
You can add extra archive types such as .rar or .zip in the same way I have allowed for different image file types. HOWEVER do test for such variations first otherwise a second run will invoke that autonaming of existing duplicates. You can of course change that -aou to -aos to avoid overwite or remove it as desired.
An alternative approach on windows is that, standard windows.zip files can be navigated and thus kept as compressed folders where a click on each file will open in the default application. Thus only need extraction to the folder when you wish to use a non default app.
The secondary advantage more directly related to your need is that some viewers can read just the images and ignore any other contents (even if they can read text or html) and one I support that reads images in zips is SumatraPDF and although not listed it can open .7Z files to show in the same way
Hence my answer to the posed question is I suggest converting 7zip to standard zip rather than expand them, or use SumatraPDF as default/alternative 7z viewer !!.
I want to create a program that can loop through multiple pdf files and have the user rename each file a unique name like so:
234324.pdf to Batch150.pdf
32154687.pdf to AdvancedPayment.pdf
and so on...
Here is my code:
setlocal EnableDelayedExpansion
echo rename pdf files
FOR %%F IN (*.pdf) DO (
set /p x=Enter:
move %%F !x!
)
endlocal
This seems to work for the first file and then when I try to rename the second one it says: The syntax of the command is incorrect..
I have tried using the rename command and haven't had much luck with it.
FOR %%F IN (*.pdf) DO (
SET "x=%%F"
set /p "x=%%F Enter: "
IF /i "!x!.pdf" neq "%%F" ren "%%F" "!x!.pdf"
)
Your code worked fine for me. Since you don't indicate what the "to" and "from" names you used were, we're reduced to guessing.
I developed the above code to perform the rename in the manner you originated. Note that it works as I expect. Using ren the "to" filename must be a filename within the directory where the "from" file resides [ie. it simply renames the file]. If you use "move" then the file can be moved to another directory if you specify that in the "to" name.
The first fix is purely cosmetic. By enclosing the variablename and prompt in the set /p in quotes, you can include a terminal space in the prompt (which I prefer), and including the %%F in the prompt shows you which file is about to be renamed.
The next fix is to quote the arguments to the ren or move. This ensures the syntax remains properly constructed in the case of eithr "to" or "from" name containing a space.
The next is to initialise x with the "from" filename. It's enclosed in quotes so any invisible trailing spaces are not included in the value assigned to x. Note that set /p does not alter the variable if Enter alone is keyed, so setting x ensures that if a file is not to be renamed, all you need do is press Enter
The next is to detect whether the "to" and "from" names are equal. ren will generate an error report if you attempt to rename a file to itself; equally, you can't move a file to itself. Hence, /i=ignore case, and only attempt the operation if the names are different.
Finally, add the .pdf to each usage of !x! in order that you don't need to key it in. Naturally, you could omit this change if you want to alter extensions or you could put .pdf into another variable and use that variable in place of the constant .pdf so that the extension being selected can be easily varied by being changed in one plaace rather than using a mass-edit. You could even use a set /p to assign the extension being processed dynamically at the start of the routine.
Note that if you rename say one.pdf to yellow.pdf then this construct is very likely to propose yellow.pdf for a rename later on. This is because the next-filename logic locates the next filename currently in the directory,then processes the loop, then locates the next filename currently in the directory, and so on.
You would need
For /f "delims=" %%F in ('dir /b/a-d "*.pdf" ') do (
to ensure that each filename is only presented once. This mechanism performs a directory scan of filenames-only in basic form, and stores the list in memory, then processes the list item-by-item and since that list is created and then processed, any subsequent alterations to the directory do not affect the list.
I need a batch file that looks in a file and moves a set of files
the problem is, is that it will need to compare files as there may be more than 1 set
the files correspond to each other
the first file in the set is BIAK1234
the second file is BIPO1234
Note the the 1234 is just the file number and will indicate what set it is a part of
So the batch file must find the set of files - BIAK1234 and BIPO1234 and move them to another file
if BIAK1234 does not exist then the script needs to end
But if there are files: BIAK1234, BIAK12345, BIPO 1234, BIPO12345 and BIPO123456
bipo123456 must remain until BIAK123456 exists and then it may be moved over
so how do I compare the files
I already know how to make a batch file move files and how to check to see if a file exists I am just not quite sure how to compare the files, determine the sets and move ONLY the sets
I struggled a bit with your question. At first I thought you were getting the file names from a text file. But your last paragraph led me to believe you are simply dealing with files in a folder.
As you say, the only moderately tricky bit is determining the individual set IDs. The solution is to use the SET command to either get a substring, or else replace the prefix with nothing. Type set /? from the command prompt to get more information.
In the solution below, I opted to use the search and replace method.
#echo off
setlocal enableDelayedExpansion
set "source=c:\yourSourceFolder"
set "destination=c:\yourDestinationFolder"
set "file1=BIAK"
SET "file2=BIPO"
pushd "%source%"
for %%F in ("%file1%*") do (
set "setID=%%F"
set "setID=!setID:%file1%=!"
if exist "%file2%!setID!" move "%%F","%file2%!setID!" "%destination%"
)
popd
I'm having trouble trying to copy and rename a file using only dos commands. I have a file of the format myfile20130218 and want to copy and rename it to some_other_file_20130218.
I know I can use copy source dest but I'm having trouble with how to isolate the date and preserve it. I cannot guarantee that he date will be today's date so that is ruled out, the source file will always be the same name.
I can run either a series of commands or a batch script, but thing that that I am currently having trouble with, is after I find a match that I need to copy, using myfile????????, how can I now get those file names to pull the dates off them?
EDIT: for clarification I will be looking at files in a known directory, as above, I will know the format of the file name, and will only be checking a specific directory for it. The process that checks the directory is a ConnectDirect file watcher, so when a file is found matching myfile20130218 I can fire off some commands, but don't know how to check the directory and get the name of the file present.
Something like this should work:
%oldname:~-8% extracts the last 8 characters from %oldname% which are then appended to the new filename.
Update: If you can identify the file with an external program and then call the batch script with the file name
copyfile.cmd C:\path\to\myfile20130218
you could do something like this:
set oldname=%~nx1
set newname=%~dp1some_other_file_%oldname:~-8%
copy "%~f1" "%newname%"
Update 2: If you know folder and the format you could call the script with the folder
copyfile.cmd C:\folder
and do something like this:
#echo off
setlocal EnableDelayedExpansion
for /f %%f in (
'dir /b "%~f1" ^| findstr /r "myfile[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"'
) do (
set oldname=%~f1\%%f
set newname=%~f1\my_other_name_!oldname:~-8!
copy "!oldname!" "!newname!"
)
endlocal
Edit: Script breakdown.
setlocal EnableDelayedExpansion enables variable expansion inside loops and conditionals.
for /f %%f in ('...') executes the command between the single quotes and then loops over the output of that command.
dir /b "%~f1" lists the content of the given directory (%~f1 expands to the full path of the first argument passed to the script) in simple mode (no header, no summary).
findstr /r "myfile[0-9]...[0-9]$" filters the input for strings that end with the substring "myfile" followed by 8 digits. The circumflex before the pipe (^|) escapes the pipe, because otherwise it would take precedence over the for command, which would effectively split the for command in half, resulting in an invalid command-line.
set oldname=%~f1\%%f assign the full path to a matching file to the variable oldname.
set newname=%~f1\my_other_name_!oldname:~-8! assign the full path to the new filename ("my_other_name_" followed by the trailing 8 digits from oldname) to the variable newname.
copy "!oldname!" "!newname!" I don't need to explain this, do I?
I'm trying to create a batch file (via Windows XP Pro) that copies 2 files (.zpl) who's filename's vary in length. ZPL files relate to label printer code. File names are as follows:
FillXferDataPBHAMFill###########.zpl
FillFormatsPBHAMFill############.zpl
The pound signs represent a number associated with a particular label/job to be printed. These numbers are identical per job. From one job to the next the numbers vary in length and always change. The directory I'm trying to pull these from contains ZPL files from multiple locations, however, I just want the BHAM ones.
The batch will copy from: \Server\C:\Directory1\Directory2\Directory3
To be copied to: \Server\Directory1\Directory2
Not sure if this will complicate things further, but the batch file will be ran from a 3rd machine. Furthermore, I do not need to copy every file everytime. Whenever new print jobs are sent, supervisors will run the batch to copy the new print jobs within the last X amount of time. X being minutes. Here is what I have so far...
#echo off
SETLOCAL enableExtensions enableDelayedExpansion
SET sourceDir=Server\C:\Directory1\Directory2\Directory3
SET targetDir=Server\Directory1\Directory2
FOR %%a (FillFormatsPBHAM*.bat) DO (
SET "filename=%%a"
SET "folder=%targetDir%"
XCOPY "%%a" !folder!
)
FOR %%b (FillXferDataPBHAM*.bat) DO (
SET "filename=%%b"
SET "folder=%targetDir%"
XCOPY "%%b" !folder!
)
:END
I apologize for a lengthy post; just wanting to be as thorough as possible. I'm learning this on the fly so bare with any ignorance on my part. Thank you in advance for ANY help!!
StackOverFlow Material Reviewed: Reference1, Reference2 -- I've been looking everywhere over the past week and these were the 2 most helpful so far.
I see some ways to fix or improve your BAT script.
The FOR command syntax is FOR %%a IN (*.bat) DO (
The sourcedir variable is set Server\C:\Directory1\Directory2\Directory3, which is not a correct path in Windows.
you initialize but don't use %sourcedir% variabe neither in your FOR loop nor in your copy commnand
you should either change the current drive and dir with a pushd %sourcedir% command, or specifying it in the FOR command.
your FOR loop assigns a %filename% variable that is never used, you may skip this assignment.
you FOR loop assigns a %folder% variable that is only then used in the copy command, you can skip this assignment and simply use %targetdir%
but, to just copy all files from one folder to the other you don't need FOR to iterate over all of them, you might just copy them right.
So, take a look at this simple script to get you started..
SET sourceDir=\\servername\sharename\Directory1\Directory2\Directory3
SET targetDir=\\anotherserver\sharename\Directory1\Directory2
xcopy %sourceDir%\FillFormatsPBHAM*.bat %targetDir%
xcopy %sourceDir%\FillXferDataPBHAM*.bat %targetDir%