Batch Script: Move files to folder based on source folder name - batch-file

I have read a couple of questions with a similar theme but none seem to cover this scenario.
I am looking to use a batch file that will check whether a directory is empty and if it contains files, move them to a corresponding folder. From here it will then check a second directory and do the same but to a different corresponding folder.
For Example:
Checks whether C:\One contains files, if it does move all the contents to C:\Receive\One
At this point (or if C:\One is empty) it will move and check whether C:\Two contains files. If it does it will move all files to C:\Receive\Two and stop the script. If C:\Two is empty the script will also stop here.
At present I'm using two scripts so was just interested as to whether there was a way to do it tidier with one script.
#echo off
for /F %%i in ('dir /b "c:\one\*.*"') do (
move C:\One\* C:\Receive\One
goto :EOF
)
echo No Files To Move

It looks to me like a simple move, in two lines:
move C:\One\*.* C:\Receive\One\
move C:\Two\*.* C:\Receive\Two\
Or, if you really need to do it in a batch-loop:
for %%a in (One Two) do (
move C:\%%a\*.* C:\Receive\%%a\
if ERRORLEVEL 1 echo No files to move from %%a
)

Related

Batch extracting specific file types from master directory with many subdirectories

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 !!.

Setting up Source and Destdir in a batch

for /L %%f in (1,1,10) do copy File1.txt %%f.txt
this code does the job very well, but I'm trying to understand how to change it to make it read
subfolders, so that I don't have to keep moving the batch file to every folder
I saw this, but not really sure how to put it together
#echo off
SET "sourcedir=C:\Users\user\Desktop\Main\Original"
SET "destdir=C:\Users\user\Desktop\Main\Copied"
for /L %%f in ('dir /s 1,1,10) do copy *.txt %%f.txt
in the section - copy *.txt %%f - I put a * so that it can only look for .txt files, but this action
slows down the coping and then stops working.
I know my code is a mess, just trying to put something together
I have many Subfolders and each folder has 1 txt file in it with all random names
and I need to make multiple copies of each file.txt in each folder
I have so many subfolders that it would literally take me months of time to get all files copied
and by then I would have many more new files to work on
so getting this copier to read Subfolders is like top priority for me.
I would like help putting this together and then explaining how it links
because I'm interested in applying the Set and dir to other batch file I have
Please any details on this will be much appreciated
I was told to look into xcopy and robocopy, but I have no idea were to add a counter
#echo off
for /1 %f in (1,1,10) do xcopy "C:\Sources" "C:\Target" /c /d /i /y
exit
So I have this that reads from source and dumps in main folder where the batch is
Option 1)
for /L %%f in (1,1,10) do xcopy "C:\Source Address\*.txt" %%f.txt
Option 2)
for /L %%f in (1,1,10) do xcopy "C:\Source Address\" "C:\Destination Address\ %%f.txt"
The thing I don't like is that is asks me a question
and I have over 10,000 txt files, I can't sit here and press F for Filename
10,000 times, can we disable that
OK, so I got this working
all I need help with is were to add this /c /d /i /y
I am still trying to get it to read Subfolders with the batch sitting
in the main folder and me not having to move files back and forth
Option 3)
for /L %%f in (1,1,110) do copy "C:\Source Address\*.txt" %%f.txt`
This works well with the Source and the wild card #magoo told me to add
the Source before the .txt file
But with this code I would still have to open hundreds of folders and move
the file to the source run the copier and then move back all copied files
Still need help with Subfolders
for /L %%f in (1,1,10) do copy File1.txt %%f.txt
will vary %%f from 1 (the first number in the parenthesised list) to 10 (the last) in steps of 1 (the middle) and will therefore copy file1.txt to 10 separate files, 1.txt to 10.txt.
Since there are no paths specified, the copy will be performed from file1.txt in the current directory to 1.txt .. 10.txt in the current directory.
If you were to put your batch file in any directory that is mentioned in the path variable (just execute path at the prompt to show it) then no matter where the current directory is, you could just use that batch filename to execute the batch, and the files would be created by copying file1.txt in the now-current directory to 1.txt .. 10.txt in the now-current directory - if file1.txt exists in the now-current directory, and if not, it will generate error messages reporting that the source file is missing.
If you were to replace file1.txt in the batch with "x:\wherever\file1.txt" then the file would be copied specifically from the file "x:\wherever\file1.txt" regardless of whether file1.txt exists in the now-current directory. (And please get used to "quoting file or pathnames" as it will avoid a whole slough of problems when you tackle names containing spaces and some other special characters).
I have no idea how for /L %%f in ('dir /s 1,1,10) do is supposed to work since according to Hoyle, the first element in the parenthesised list should be a number. I'd suggest that we have a small transcription problem here.
Th slower and slower problem - yes, understandable. You are creating more and more .txt files, and copying all of them to a new destination...
Perhaps referring to This response and question might show how to solve your subdirectory-scan issue.

Open multiple directories without knowing the names of said directories and delete folders within them using a Windows batch file

Okay, I do not really know how to describe fully what I want and I feel bad for asking something so advanced. I do not know how to do anything other than move folders and delete files and folders using batch.
I am trying to make a batch file to delete certain Steam files and folders which are used to cache data. I could easily make this for myself and call it a day however I would like to share it and have it work for other people also. The problem is one of the cache folders is named with a unique Steam identifier. I will put what I have made so far below.
REM Makes a temporary folder.
mkdir %LocalAppData%\Temp\steam_cache
REM Deleted all none essential files in Steam's configuration folder.
move "%ProgramFiles(x86)%\Steam\config\config.vdf" %LocalAppData%\Temp\steam_cache
move "%ProgramFiles(x86)%\Steam\config\loginusers.vdf" %LocalAppData%\Temp\steam_cache
rmdir "%ProgramFiles(x86)%\Steam\config" /s /q
mkdir "%ProgramFiles(x86)%\Steam\config"
move %LocalAppData%\Temp\steam_cache\config.vdf "%ProgramFiles(x86)%\Steam\config"
move %LocalAppData%\Temp\steam_cache\loginusers.vdf "%ProgramFiles(x86)%\Steam\config"
REM Delete all none essential files in Steam's userdata folder.
I will put what the file structure goes like below.
%ProgramFiles(x86)%\Steam\userdata\256283931
As you can see the number at the end is completely random and I do not know how to open that directory without knowing the number first. There are sometimes multiple of these folders with random names if you login with multiple account and I would like the batch file to go into them one by one and delete certain folder inside of them.
I will put below the folders that I would like to delete that are inside of the random numbered folder below.
%ProgramFiles(x86)%\Steam\userdata\256283931\ugcmsgcache
Sorry if what I am asking it too much if so just ignore this post, thanks.
What you are looking for is the for /D loop, which enumerates directories:
rem // Enumerate all directories under `userdata`:
for /D %%I in ("%ProgramFiles(x86)%\Steam\userdata\*") do (
rem // Check if there is really a sub-directory called `ugcmsgcache`:
if exist "%%~I\ugcmsgcache\" (
rem // Do something with the full path, like echoing, for instance:
echo "%%~I\ugcmsgcache"
)
)
If you want to ensure that the name of the enumerated directory/-ies name consist(s) of decimal figures only, you could use dir and findstr, together with a for /F loop:
rem // Change into parent directory, because `dir /B` returns plain directory names:
cd /D "%ProgramFiles(x86)%\Steam\userdata"
rem // Enumerate all directories under `userdata`, whose names are pure numbers:
for /F "delims=" %%I in ('
dir /B /A:D "*" ^| findstr /I /X "[0123456789]*"
') do (
rem // Do something with the full path, like echoing, for instance:
echo "%%~fI\ugcmsgcache"
)
The cd command could also be replaced by pushd and popd.
The ~f modifier ensures that the full path is derived, related to the current working directory.
Note that the pipe (|) needs to be escaped here, because it is in the set of the for /F loop.

Recursively search for FOLDERS from list and move

Need help with searching all sub folders. The current script works fine but only in the top directory. Any help?
md "C:\users\%username%\desktop\People"
SET LOGFILE=C:\users\%username%\desktop\movelog.txt
for /f "delims=" %%i in (people.txt) do (
move "%%i" C:\users\%username%\desktop\People >nul 2>&1
if errorlevel 1 (
echo %%i : Move failed >> %LOGFILE%%
) else (
echo %%i : Move successful >> %LOGFILE%
)
)
Assuming that im reading your question correctly, you want to be able to use the script from any location to find a folder thats specified within your text file to be moved to the new location correct? On linux machines a simple find * "foldername"|grep "foldername" would present usable code to find the files your looking for. Windows does have something similar but not nearly as easy as the linux version.
You can use the following code dir \ /A:D /B /S /O:G|find "%%i" to search for the folder recursively. However the code will output ALL instances of the particular string thats passed to it, which includes the file path for EVERYTHING thats in the particular folders contents. What you would be asking is to move all folders under that name to the new location.
Because of this, there needs to be some sort of identifier in each users folder as so to let the sorting program know that this is what im looking for and nothing else. If i had a blank text file labeled marker.txt in all the particular folders, i can run the code as dir \ /B /S|find "%%i\marker.txt" and this provides a unique marker for the searching program to identify with. This can be stored and sorted later into a variable so for example i had a marker.txt I can then run a simple set path=dir \ /B /S|find "%%i\marker", and make the new movement variable as move %path%\... So unless you have a file that is commonly shared by all of these, you may find it difficult to sort them.

batch Moving all files in 100s of folders up one folder

I have a small issue. I'm not super fluent in batch scripting but i'm trying to get better.
I'm trying to get a script working at my current job that we can use to move all files in the last sub-directory of a parent up one directory.
The need is for moving image sequences delivered with a superfluous sub-directory up one sub-directory for effects workflow
essentially i'm being delivered 100s of image sequences as:
Episode/Footage/shot/resolution/shot.0000001.dpx
I would love a batch script i could move into the "Footage" folder that would parse the shot directories below them and move the image sequences out of the resolution folder and place them directly into the shot folder so the structure would look more like this:
Episode/Footage/shot/shot.0000001.dpx
This batch would need to be reused over many instances and just be robust enough to be reusable regardless of what the extensions on the image sequences are or the naming structure of the subfolders are.
for /f "delims==" %%i in ('dir /a:d /b') do for /f "delims==" %%f in ('dir "%%i" /a:d /b') do (move "%%i\%%f\*" "%%i"&&rd "%%i\%%f" /s /q)
The * can be replaced with * . * (no spaces) to only grab files.
Use % instead of %% for use on the command line.
This will grab from any dir under Footage. Delete the first for loop and replace all occuences of %%i with shot to only grab from there.
#!/bin/bash
mv Episode/Footage/Shot/*/* Episode/Footage/Shot
Or for more Fanciness:
#!/bin/bash
echo "Write the file path:"
read file_path
mv $file_path/*/* $file_path/
This will ask you what the path is (This is Episodes/Footage/shot in your example) then find all the files in that path and all the files inside them and put them all in the path.
You could also modify this if instead of putting the files in the path you want them in a different place you could add:
echo "Write the destination file path:"
read file_path2
in between: read file_path and mv ...
and change:
mv $file_path/*/* $file_path
to:
mv $file_path/*/* $file_path2
There is really no need to go through so much trouble.
With the exceptions of the PUSHD and POPD commands, the whole thing can be placed on one line like nephi12 did.
I'm puting everything on separate lines for clairity's sake, but will give two examples of it on one line later.
REM Set working directory
PUSHD C:\Episode\Footage\
REM Find all the sub directories of the working
REM directory, and if any exist execute the loop below:
For /d %%d in ( *.* ) do (
REM Move all files inside the subdirectory up one level.
move /s %%d\*.* %%d\..\.
REM Delete the directories if they are empty.
RD /y %%d
)
REM Return to your original working directory.
POPD
The above code pushes the current directory name into a stack, and changes dirs to Footage.
Then the For /d command grabs all (if any) of the sub directories of Footage, and feeds them one at a time first into the MOVE command, then into the Remove Directory command.
The MOVE /s command moves all files in directory %%d (including any in sub folders) up one level.
Next the RD /y command automatically deletes the now empty directories, or causes a soft error if they are not empty.
The final command returns the working directory to it's original location, POPing the path off the stack.
On one line, without the PUSHD and POPD commands and the directory cleanup, it looks like this:
Single Line Solution:
For /d %%d in ( *.* ) do move /s %%d\*.* %%d\..\.
Single Line Solution with Cleanup:
To include the cleanup just 'add' the and command && after the move command and before the RD, like so:
For /d %%d in ( *.* ) do ( move /s %%d\*.* %%d\..\. & RD /y %%d )
Why I did what I did.
The fastest way to move a whole bunch of files, is not to tell move how and where to move each file, but to use wild cards that will allow Move to figure out by it'self where to put them. So rather than tell it to move one file at a time, I give it wild cards, which allowsMOVE to work at it's fastest. So I only feed it the individual source and destination directories, and say "copy ALL files from here to there".
A set of brackets (...) can hold, what the interpreter considers a single line of code. Which causes a whole new set of problems, but it allows a special variable like %%d to exist on (what to us appears to be) multiple lines of code. I did that at the top. Later, I used another set of brackets to show the command interpreter what is part of a single line is inside a FOR statement, and what is not.
I also used a bit of old DOS shorthand to specify a parent directory, .. which allows me to not know the exact path of something, and still manipulate things in it's parent . As an example,C:\WINDOWS\.. is C:\, and C:\WINDOWS\SYSTEM32\.. is C:\WINDOWS\ .
You might notice that I also used the file specification . . That is probably unnecessary, but I've ran into enough trouble without it that I use it. It just makes clear to MOVE that we are moving these files to a folder, not a file. And, oh yes, MOVE will gladly move hundreds of your files on top of each other, ruining them all but the last one. You can imagine how I came to embrace the . .
The & allows you to string multiple commands, or programs, onto a single line of code, and allows me to tack the RD to the end of the line, and still have access to the special variables that the FOR command created.
So, now if you were paying attention, you'd have realized that my second sentence was a fib. There is nothing stopping you from tacking a PUSHD & and a & POPD onto the beginning and end of that line.

Resources