I am looking for help to construct a .bat script to move PDFs into a predefined folder structure. The file names are structured and relate to where in the folder structure they should be moved to.
For example IRTYCAS001.pdf;
First two letters tell it to move it to the correct country folder (Ireland)
3rd and 4th will tell it what county folder to move it to
5th to 7th will tell it the correct town folder to move it to and
Last 3 digits tell it the land use type folder to move it to
The identifier lengths will always be the same in the file name.
The folder structure looks something like
example of folder structure
With extensions enabled (default) mkdir will create intermediate folders in one step.
So all you've to do is
iterate the files
use substrings to split the filename into parts and
create the folders if not already existing.
#echo off & setlocal EnableDelayedExpansion
set Src=A:\
set Dst=A:\
for /f "delims=" %%A in ('Dir /B "%Src%*.pdf"') do (
Set "File=%%A"
set "Folder=%Dst%\!File:~0,2!\!File:~2,2!\!File:~4,3!\!File:~7,3!\"
if not exist "!Folder!" MD "!Folder!" >NUL
Move "%%A" "!Folder!"
)
Sample tree:
> tree . /f
A:\
└───IR
└───TY
└───CAS
└───001
IRTYCAS001.pdf
you could try something like this:
FOR /F %%i IN ('dir /b c:\temp\*.pdf') DO call :moveFiles %%i
goto :EOF
:moveFiles
set myfile=%1
set part1=%myfile:~0,2%
set part2=%myfile:~2,2%
set part3=%myfile:~4,3%
set part4=%myfile:~7,3%
set dstFolder=C:\temp
if %part1%==IR set dstFolder=%dstFolder%\ireland
REM more options here...
if %part2%==TY set dstFolder=%dstFolder%\tipperary
REM more options here...
if %part3%==CAS set dstFolder=%dstFolder%\cashel
REM more options here...
if %part4%==001 set dstFolder=%dstFolder%\residential
REM more options here...
move /Y %myfile% %dstFolder%
Related
At work we use a bespoke program to search through a directory tree and find an individual image file. These files are stored in folders that have a 7-digit name, starting with '18' - so for instance '1873456', '1873457', '1873458' etc. The problem I have is at some point last year the procedure that creates these folders and populates the images in them reached '1899999' - and then rolled over to '18100000' and carried on like that for over 4,000 folders before it was caught and corrected.
The bespoke program we use can only handle seven-digit folder names. What I would like to do is create a batch file that renames all the eight-digit folders by removing the extra '1' in the name, so '18100000' becomes '1800000' and so forth until '18104013' becomes '1804013'.
Can anyone help?
Run this in the base folder, it will not change any folders.
A file called renfile.bat.txt will be created that contains the rename command for the folders that match the filespec. Examine it in notepad to see if it is ok and then rename it to renfile.bat and run it.
It's not tested.
#echo off
setlocal enabledelayedexpansion
for /d /r %%a in (18??????) do (
set "name=%%~nxa"
>>renfile.bat.txt echo ren "%%a" "!name:~0,2!!name:~3!"
)
Something like
for /l %%x in (100000,1,104013) do (
set oldsuffix=%%x
set newsuffix=%oldsuffix:~-5%
ren 18%%x 18%newsuffix%
)
setlocal enableextensions enabledelayedexpansion
for /d /r "c:\somewhere" %%f in (181?????) do (
set "name=0" & set /a "name+=%%~nf" 2>nul
if !name! gtr 1899999 ren "%%~ff" "18!name:~-5!"
)
I have a folder that gets a new file added everyday to the folder with the same file name but incremental extension such as .001, .002, .003, etc. However, if there's no file within the folder it starts at .001 again.
The problem is they are all named the same and if I move them to another folder to archive them it would just overwrite the same file over and over again. I could create a folder each day with the date with only one file in it, but that seems a bit redundant.
Is there a way to look at the create date of each file and rename it to the create date?
I've gotten this far, but it looks like for this situation I have to use a static file name, how to loop through the entire directory?
SET filename = C:\test.001
FOR %%f IN (%filename%) DO SET filedatetime=%%~tf
rename c:\test.001 C:\test_%filedatetime%.txt
move C:\*.txt C:\archive\
this provides the correct sort order:
#echo off &setlocal disableDelayedExpansion
set "startfolder=%userprofile%\test"
cd /d "%startfolder%"
for %%a in (*) do (
for /f "delims=." %%b in ('wmic datafile where "name='%startfolder:\=\\%\\%%~a'" get lastmodified^|find "."') do (
echo(ren "%startfolder%\%%~a" "%%~b.txt"
)
)
Remove echo to get it working.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "targetdir=c:\sourcedir"
SET "destdir=c:\destdir"
PUSHD "%targetdir%"
FOR %%a IN (*.*) DO (
SET "timestamp=%%~ta"
SET "timestamp=!timestamp:/=_!
SET "timestamp=!timestamp::=_!
SET "timestamp=!timestamp:.=_!
SET "timestamp=!timestamp:,=_!
SET "timestamp=!timestamp: =_!
ECHO MOVE "%%a" "%destdir%\%%~na.!timestamp!"
)
GOTO :EOF
This should work with any file in the nominated target directory where the name does not include ! or ^.
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 gymnastics around timestamp are intende to replace /: with _ since these are illegal filename characters. Space., are similarly replaced - they're legal but often painful.
If you want the destination filename to be name.003.timestamp, remove the ~na from the destination name.
Try like this :
SET $path=The_path_who_contain_the_FILES
FOR /F "DELIMS=" %%f IN ('dir "%$path%" /a-d/b') DO (
SET filedatetime=%%~tf
move "%%~dpnxf" "C:\archive\test_%filedatetime%.txt")
I have managed to copy and manipulate a batch script that does the following:
looks at the last 4 characters of a .csv file
creates a folder with this name
moves the file into the folder
#echo off & setlocal EnableDelayedExpansion
echo.
pushd "%~dp0"
for %%j in (*.csv) do (
set file=%%~nj
set folder=!file:~-4!
if !folder!==FI_2 set folder=!file:~-6!
if not exist !folder! md !folder!
move "%%~j" "!folder!"
echo "%%~j" -^> "!folder!"
)
popd
echo.& echo.Done
:: End_Of_Batch
The problem I have is that I need to group the files by month. The file names look like this:
BS_IDX_LEVEL_YYYYMMDD_BAFI.csv
(obviously the YYYYMMDD will be replaced with the date of the file)
So any file with the name BS_IDX_LEVEL_20111231_BAFI.csv would go into a folder named "1112" (date formate of folder is YYMM)
A file with the name BS_IDX_LEVEL_20111115_BAFI.csv would go into a folder names "1111"
Is there someway I can alter this script so that before organising the files by their filenames it groups them by date first?
If I can explain anything further please let me know
While you are looping through and getting the last 4 characters of the file name, you can also look at the dates. I didn't know if the start of the file will be fixed or subject to change, but I thought it reasonable to assume the date_end syntax would be consistent.
set filedate=!file:~-9,-7!
This will extract the characters between 9th and 7th from the end of the String stored in !file!. With the syntax you have, the will be the two digit month number. This will also avoid any confusion with 2 or 4 digit dates.
Once you have both this and the other folder name, you can simply create both folders at once and place the file directly into the subfolder, so you will still only need to make one pass over the fileset.
#echo off & setlocal EnableDelayedExpansion
echo.
pushd "%~dp0"
for %%j in (*.csv) do (
set file=%%~nj
set folder=!file:~-4!
if !folder!==FI_2 set folder=!file:~-6!
set filedate=!file:~-9,-7!
SET datefolder=!folder!\!filedate!
if not exist !folder! md !folder!
if not exist !folder!\!filedate! md !folder!\!filedate!
move "%%~j" "!datefolder!"
echo "%%~j" -^> "!datefolder!"
)
popd
echo.& echo.Done
:: End_Of_Batch
The code below works fine, here is a list of it's functions:
It moves files based on the fist 4 characters to a pre-created folder with the same first 4 characters
If the folder does not exist, it will not move the file, as there is no folder with the same fist 4 chars.
#echo on
setlocal enabledelayedexpansion
cls
pushd R:\Contracts\Sites
for /f "tokens=*" %%1 in ('dir /a-d /b *') do (
set filename=%%1&set dirname=!filename:~0,4!
for /f "tokens=*" %%A in ('dir /ad /b') do (
set dirid=%%A & set dirid=!dirid:~0,4!
if "!dirid!" equ "!dirname!" move %%1 %%A
)
)
I would like to add one extra function to this code please. Pleas have a look at the example below.
I have 5 files
X32A-test.docx or X32A-test.pptx (there will only be one docx or pptx, "NEVER two with the same name")
X32A-test.pdf
X32A-test.avi
X32A-test-eng.sub
X32A-test-small.jpg
I would like the code to CREATE a folder if it does not exist, based on the file name if it has the extension docx or pptx.
So with the above example it would create a folder named: "X32A-test". Then all the other files with "X32A" in the front of the name will be moved to that newly created folder "X32A-test".
I hope it is clear enough. If not please ask me for more information.
Thank you
It is much simpler and more efficient to use the simple FOR instead of FOR /F in your case.
And rather than looping through every file and moving them individually, it is easier and more efficient to use wildcards.
The first loop finds the .pptx and .docx files and creates folders as needed
The second loop finds all the directories and moves all files that start with the directory name into the directory.
#echo on
setlocal enableDelayedExpansion
cls
pushd R:\Contracts\Sites
for %%F in (*.docx *.pptx) do (
set "folder=%%F"
2>nul md !folder:~0,4!
)
for /d %%F in (*) do move %%F* %%F
popd
If needed, you can protect yourself against directory names shorter than length 4.
#echo on
setlocal enableDelayedExpansion
cls
pushd R:\Contracts\Sites
for %%F in (*.docx *.pptx) do (
set "folder=%%F"
set folder=!folder:~0,4!
if !folder:~0,3! neq !folder! 2>nul md !folder!
)
for /d %%F in (????) do (
set "folder=%%F"
if "!folder:~0,3!" neq "%%F" move %%F* %%F
)
popd
Note that this solution may fail if a file name contains !. If that arises then you need to toggle delayed expansion on and off within the loop(s).
I can see the entire process (including the part already implemented) like this:
All the files that are not yet in their "home" directories are moved there.
For all .docx and .pptx files left, create directories based on the files' names.
Obviously, the step #2 creates new "homes" and those will still be "uninhabited" this far. So all that is left to do now is to repeat the step #1.
So I would probably reorganised your process and, with the additional requirement, it could be implemented this way:
…
PUSHD your_root_directory
FOR /D %%D IN (*) DO (
CALL :movefiles "%%D"
)
FOR %%F in (*.docx *.pptx) DO (
MKDIR "%%~dpnF"
CALL :movefiles "%%~dpnF"
)
…
GOTO :EOF
:movefiles
SET "dirname=%~n1"
SET "mask=%dirname:~0,4%*"
MOVE "%~dp1%mask%" %1
Note: The steps #2 and #3 could be either implemented as separate loops or combined in one. The above script uses the latter approach.
You can use negative offsets in the !var:~offset,len! evaluation as follows:
set fspec=X32A-test.docx
echo !fspec:~-10!
echo !fspec:~0,-10!
That second line above gives you -test.docx and you can simply check that against your two desired possibilities with an if statement (or two).
Then you can use the third line to get the rest of the name for creating a directory.
The following example script shows how this could be done:
#setlocal enableextensions enabledelayedexpansion
#echo off
set fspec=X32A-test.docx
set bit1=!fspec:~-10!
set bit2=!fspec:~0,-10!
if .!bit1!.==.-test.docx. echo mkdir !bit2!
if .!bit1!.==.-test.pptx. echo mkdir !bit2!
endlocal
I'm echoing the mkdir command rather than executing it so you need to take out the echo. You'll also need to integrate the set and if statements into your loop but, based on what you have so far, you should have little trouble with that.
If, as you seem to indicate in a comment, the first four characters are the key and the last five decide on whether to make the directory, as in:
x32s-test.docx
a21w-production.pptx
xxxx-whatever_the_blazes_you_want.some_other_rubbish.docx
Then you're really only interested in the first four and last five:
#setlocal enableextensions enabledelayedexpansion
#echo off
set fspec=a12b-whatever_the_blazes_you_want.some_other_rubbish.docx
set bit1=!fspec:~-5!
set bit2=!fspec:~0,4!
if .!bit1!.==..docx. echo mkdir !bit2!
if .!bit1!.==..pptx. echo mkdir !bit2!
endlocal
This checks the correct extensions and outputs:
mkdir a12b
as expected.
Here's my situation. A project has as objective to migrate some attachments to another system.
These attachments will be located to a parent folder, let's say "Folder 0" (see this question's diagram for better understanding), and they will be zipped/compressed.
I want my batch script to be called like so:
BatchScript.bat "c:\temp\usd\Folder 0"
I'm using 7za.exe as the command line extraction tool.
What I want my batch script to do is to iterate through the "Folder 0"'s subfolders, and extract all of the containing ZIP files into their respective folder.
It is obligatory that the files extracted are in the same folder as their respective ZIP files. So, files contained in "File 1.zip" are needed in "Folder 1" and so forth.
I have read about the FOR...DO command on Windows XP Professional Product Documentation - Using Batch Files.
Here's my script:
#ECHO OFF
FOR /D %folder IN (%%rootFolderCmdLnParam) DO
FOR %zippedFile IN (*.zip) DO 7za.exe e %zippedFile
I guess that I would also need to change the actual directory before calling 7za.exe e %zippedFile for file extraction, but I can't figure out how in this batch file (through I know how in command line, and even if I know it is the same instruction "cd").
EDIT #1
I have already received some tips on ServerFault to the same question. Please see the answers at this link.
However, it extracted from the root (C:), and not from the given in parameter folder.
Anyone has an idea?
EDIT #2
It seems that batch script doesn't handle folder and file names containing a space character adequately. Can anyone confirm what I think?
EDIT #3
I need it to be fully recursive, since I don't know the directory structure against which this will be used.
EDIT #4.a
With #aphoria's solution, I'm almost there! The only problem is that it takes let's say File5.zip, retrieve the filename only to get File5, creates a subfolder File5 and extract the File5.zip to File5 subfolder, then, it wants to create a File5 subfolder in Folder 1, where it should instead want to create File1 subfolder, to stick with my example.
EDIT #4.b
As required, here's the code as it currently look:
#echo off
setlocal enableextensions enabledelayedexpansion
rem
rem Display instructions when no parameter is given.
rem
if "%1" equ "" (
echo Syntaxe : od.bat ^<directory mask>^
echo Exemple : od.bat *
goto :Eof
)
rem
rem Setting the PATH environment variable for this batch file for accessing 7za.exe.
rem
path=c:\temp;%PATH%
rem
rem Removing quotes from the given command line parameter path.
rem
set root=%1
set root=%root:~%1
set root=%root:~0,-1%
rem Searching directory structure from root for subfolders and zipfiles, then extracting the zipfiles into a subfolder of the same name as the zipfile.
for /F "delims==" %%d in ('dir /ogne /ad /b /s %root%') do (
echo Traitement du dossier : "%%d"
for /F "delims==" %%f in ('dir /b "%%d\*.zip"') do (
rem Getting filename without extension.
set subfolder=~n%f
mkdir "%%d\%subfolder%"
rem Extracting zipfile content to the newly created folder.
7za.exe e "%%d\%%f" -o"%%d\%subfolder%"
)
)
:Eof
endlocal
Ideas anyone?
My guess is that it digs one directory hierarchy at a time. Here's the deal. Consider we have a Folder A in Folder 1 (Folder 1\Folder A), then, it searches from Folder 1 through Folder 5, and comes back to Folder 1\Folder A, where the %subfolder% variable sticks with its last value.
Anyone's help is gratefully appreciated.
I'm not very familiar with the 7zip command-line options, so you will need to figure out the exact command for that, but the script below will take a fully specified path (spaces allowed) and print out the the folder name and .zip files contained within it.
#ECHO OFF
REM
REM Remove the double quotes from the front and end of the root path
REM
SET ROOT=%1
SET ROOT=%ROOT:~1%
SET ROOT=%ROOT:~0,-1%
ECHO %ROOT%
FOR /F "DELIMS==" %%d in ('DIR "%ROOT%" /AD /B') DO (
ECHO %%d
FOR /F "DELIMS==" %%f in ('DIR "%ROOT%\%%d\*.zip" /B') DO (
ECHO %%f
)
)
Run it like this:
MyScript.CMD "c:\temp\usd\Folder 0"
You should get output similar to this:
Folder A
File 1.zip
File 2.zip
Folder B
File 1.zip
File 2.zip
UPDATE
The code below will extract Folder A\File 1.zip to a new folder Folder A\File 1.
A few things to keep in mind:
In the first FOR loop, you need to have %ROOT% enclosed in double quotes to handle folders with spaces in the name.
Since you're SETting a variable inside the second FOR, you need to put SETLOCAL ENABLEDELAYEDEXPANSION at the beginning. Then, reference the variable using ! (for example, !subfolder!) to force expansion at runtime.
This line of your code set subfolder=~n%f should be this set subfolder=%%~nf
I put ECHO in front of the MKDIR and 7za.exe commands to test. Once you are sure it is doing what you want, remove the ECHO statement.
Here is the code:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM
REM Remove the double quotes from the front and end of the root path
REM
SET ROOT=%1
SET ROOT=%ROOT:~1%
SET ROOT=%ROOT:~0,-1%
ECHO %ROOT%
REM Searching directory structure from root for subfolders and zipfiles,
REM then extracting the zipfiles into a subfolder of the same name as the zipfile.
FOR /F "delims==" %%d IN ('dir /ogne /ad /b /s "%ROOT%"') DO (
ECHO Traitement du dossier : "%%d"
FOR /F "delims==" %%f IN ('dir /b "%%d\*.zip"') DO (
REM Getting filename without extension.
SET subfolder=%%~nf
ECHO mkdir "%%d\!subfolder!"
REM Extracting zipfile content to the newly created folder.
ECHO 7za.exe e "%%d\%%f" -o"%%d\!subfolder!"
)
)
ENDLOCAL