Create Folder and SubFolder based on file name using batch file - batch-file

Coding is not my speciality but I have come across a problem and google search has guided me to using batch file process in solving it. Essentially I have a couple of thousands of files that need to be moved into folders and they have a very simple file structure, listed below:
UK--London--Filename.pdf
UK--London--Filename2.pdf
UK--Manchester--Filename3.pdf
UK--Liverpool--Filename4.pdf
UK--Chester--Filename5.pdf
I would like the script to:
1. Pick up the first "--" check if the folder exists, if not create it
2. Pick up the second "--" check if the folder exists, if not create it
3. As there might be more than two "--", ignore the rest
4. Move file into the subfolder
To that end, the output should be some like (note "FILETEST" is the folder I am using to test the script):
C:\FILETEST\UK\London\UK--London--Filename.pdf
C:\FILETEST\UK\London\UK--London--Filename2.pdf
C:\FILETEST\UK\Manchester\UK--Manchester--Filename3.pdf
C:\FILETEST\UK\Liverpool\UK--Liverpool--Filename4.pdf
C:\FILETEST\UK\Chester\UK--Chester--Filename5.pdf
I have had an attempt to re-using a script from another question in stackoverflow (Batch create folders based on part of file name and move files into that folder). I understand that it would not do exactly what I need, but cant seem to get any output.
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\FILETEST"
PUSHD %sourcedir%
FOR /f "tokens=1*delims=--" %%a IN (
'dir /b /a-d *.*.*'
) DO (
ECHO MD %%a
ECHO MOVE "%%a.%%b" --\%%a\
)
POPD
GOTO :EOF
Apologies for any headaches caused, I am hoping this is a simple one to solve.
Thank you,
Panos

#ECHO OFF
SETLOCAL
SET "sourcedir=c:\FILETEST"
PUSHD %sourcedir%
FOR /f "tokens=1,2*delims=-" %%a IN (
'dir /b /a-d *--*--*.*'
) DO if "%%c" neq "" (
ECHO MD "%%a"
ECHO MD "%%a\%%b"
ECHO MOVE "%%a--%%b--%%c" ".\%%a\%%b\"
)
POPD
GOTO :EOF
Read the directory list of files in the current directory, (/a-d = no directorynames) that match *--*--*. Tokenise so that %%a acquires the part before the first --sequence, %%b the second and %%c the remainder.
If %%c is not empty then make the directories ".\%%a" and ".\%%a\%%b" (quoted because any spaces in the name would otherwise be seen as "create two directories") then move the file, again quoted for the same reason.
Note that each character individually between delims= and the close-quote is a delimiter - a delimiter-string is not supported. Consequently, this code will pick up - as well as --- and any other sequence of - and try to process it. You could gate the create/move further by adding if exist "%%a--%%b--%%c" directly after the if "%%c" neq ""before the (.
The md will create a directory if the target name does not already exist, and produce an error-message if it already exists. To suppress the error message, append 2>nul to the md lines.

Related

MD command creating generic file instead of a directory

I've written a simple batch file to move .pdf files to certain folders located on a file system. Currently, the code checks to see if a folder with a company's name exists (P:\invoices\%company%). If the folder exists then the code checks to see if a subfolder called 2021 is located at P:\invoices\%company%\2021. If the 2021 folder already exists, the code moves the .pdf file to that 2021 folder.
However, if the 2021 subfolder does not exist, then the code is supposed to create a 2021 folder at P:\invoices\%company%\2021. I used the md command to do this but it creates a generic file called 2021 instead of a folder. Then the .pdf file disappears into thin air, presumably because it is moving it into a file instead of a folder.
Here is the code:
#echo off
for /r "C:\Users\me\Scan\*.pdf" %%G in (*.pdf) do set "name=%%~nG"
for /f "tokens=1-2 delims=-" %%i in ("%name%") do set "company=%%i"
if exist "P:\invoices\%company%" do (
if exist "P:\invoices\%company%\2021" do (
move /y "C:\Users\me\Scan\*.pdf" "P:\invoices\%company%\2021"
)
else do (
md "P:\invoices%\company%\2021"
move /y "C:\Users\me\Scan\*.pdf" "P:\invoices%\company%\2021"
)
)
What am I doing wrong?
#echo off
:: This ensures that the original environment is restored on termination
SETLOCAL
:: Use variable so year needs to be changed in only 1 place
set "year=2021"
:: And representing destination parent-directory
set "destparent=P:\invoices"
:: read the filenames from the tree
for /r "C:\Users\me\Scan\*.pdf" %%G in (*.pdf) do (
REM %%G has full filename, %%~nG the name part only
for /f "tokens=1 delims=-" %%i in ("%%~nG") do (
REM %%i has company name
REM Optional to auto-create company-directory begin
REM force Company directory to exist
MD "%destparent%\%%i" 2>nul >nul
REM Optional to auto-create company-directory end
REM If company directory truly exists
if exist "%destparent%\%%i\." (
rem force year subdirectory to exist
MD "%destparent%\%%i\%year%" 2>nul >nul
rem does subdirectory now exist?
if exist "%destparent%\%%i\%year%\." (
move /y "%%G" "%destparent%\%%i\%year%\"
) else (
echo "%destparent%\%%i\%year%" directory not found
)
) else (
echo "%destparent%\%%i" directory not found
)
)
)
[Untested - apply to dummy directories for verification]
OP code problems:
The for /r ... %%G will assign to name the name of the LAST file encountered, which will be the from the very last directory in the subtree that contains a file passing the name-mask *.pdf Then the value installed in name is only used to derive the company-name.
company is set up from the part before the first - in name. The part between the first and second - in name (or end of name) is assigned to %%j and never used. OP should consider the possibility that the actual company-name is hyphenated.
OP's code uses if ... DO ( (more than once). DO is illegal here and must be omitted.
OP's code uses else ... DO ( . DO is illegal here and must be omitted. The else keyword must be on the same physical line as the ) for the true code block (parenthesised sequence of statements)
The MOVE statements attempt to move ALL of the .pdf files in the selected subtree-rootdirectory to the destination, regardless of file's company-code or the fact that the selected company-code may be from the .pdf at the deepest level of the tree.
I've attempted to fix the code to do what I suspect the actual intention of the code is, being to move the .pdf files from the subtree to each file's appropriate destination\company\2021 directory, creating directories as necessary.
I will emphasise one again that this code is UNTRIED.
As a suggestion for testing, on a copy of the batch, I would replace each MD and MOVE statement with ECHO MD and ECHO MOVE respectively, and omit all of the redirection statements 2>nul and >nul (which are intended to suppress unwanted messages)
Finally, I'd insert a PAUSE line before the final goto :eof to permit the resultant proposed-list-of-changes from disappearing as I suspect that we may be dealing with a case of Heretical Clickitis.
Here's how I'd suggest you try the task:
As a batch-file:
#For /R "%UserProfile%\Scan" %%G In (*-*.pdf
) Do #For /F "Delims=-" %%i In ("%%~nG"
) Do #%SystemRoot%\System32\Robocopy.exe "%%~dpG." "P:\invoices\%%i\2021" "%%~nxG" /Mov 1>NUL
Or, as a single line entered in cmd:
For /R "%UserProfile%\Scan" %G In (*-*.pdf) Do #For /F "Delims=-" %i In ("%~nG") Do #%SystemRoot%\System32\Robocopy.exe "%~dpG." "P:\invoices\%i\2021" "%~nxG" /Mov 1>NUL
Just change the paths, %UserProfile%\Scan and P:\invoices, as needed.
[Edit /]
If you want you own code fixing, according to the comments I've already made, and to fix your two previously unmentioned typos md "P:\invoices%\company%\2021" and move /y "C:\Users\me\Scan\*.pdf" "P:\invoices%\company%\2021, which should have read md "P:\invoices\%company%\2021" and move /y "C:\Users\me\Scan\*.pdf" "P:\invoices\%company%\2021 respectively. Then this is how I'd suggest you could have fixed it:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /R "C:\Users\me\Scan" %%G In (*.pdf) Do (
Set "name=%%~nG"
SetLocal EnableDelayedExpansion
For /F "Delims=-" %%i In ("!name!") Do (
EndLocal
Set "company=%%i"
)
SetLocal EnableDelayedExpansion
If Exist "P:\invoices\!company!\" (
If Not Exist "P:\invoices\!company!\2021\" (
MD "P:\invoices\!company!\2021"
)
If Exist "P:\invoices\!company!\2021\" (
Move /Y "%%G" "P:\invoices\!company!\2021"
)
EndLocal
)
Pause
Note: As you are both creating and using variables within the same parenthesized code blocks, I have used delayed expansion to ensure that your updated variables are maintained throughout.
Please now compare my upper answer with your fixed version below it, and see why, I offered the much shorter and simpler solution initially.

Batch file to Move files based on part of filename, to folder based on part of folder name

Found a pyhton solution here, but I need a batch file-based solution.
Have many files:
SSP4325_blah-blah-blah.xml
JKP7645_blah.xml
YTG6457-blah-blah.xml
And folder names that contain a piece of the file name:
RefID - SSP4325, JKP7645, GHT1278, YRR0023
RefID - YTG6457
I'm looking for a batch solution which would read a portion of the file name at the front (before either the first dash or underscore) and then move that file into the folder where the front of the filename exists as part of the folder name.
So in the above examples, the first two files (SSP4325 and JKP7645) were moved into the first folder because it contained it contained that text as part of the folder name.
The third file would be moved into the second folder.
I have hundreds of files and 63 folders. So I'm hoping to be able to automate.
Can't use Powershell or Python due to limitations of the environment. So hoping for a batch file approach.
Thanks. Sean.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.xml" '
) DO (
FOR /f "tokens=1delims=_-" %%b IN ("%%a") DO (
FOR /f "delims=" %%d IN (
'dir /b /ad "%destdir%\*%%b*" '
) DO (
ECHO(MOVE "%%a" "%destdir%\%%d\"
)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
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)
After establishing the directories, the outer loop puts the filename in %%a, the next loop gets the first part of that name, up to but not including the first - or _ (the delims specified) into %%b.
The inner loop finds the target directory containng %%b in the destination directory and constructs an appropriate move line.
This solution review the folders just one time and store they in an array, so this method should run faster.
#echo off
setlocal EnableDelayedExpansion
rem Process the folders
set i=0
for /D %%a in (*) do (
rem Store this folder in the next array element
set /A i+=1
set "folder[!i!]=%%a"
rem Separate folder in parts and store the number of the array element in each one
for %%b in (%%a) do set "part[%%b]=!i!"
)
rem Process the files
for %%a in (*.xml) do (
rem Get the first part of name
for /F "delims=-_" %%b in ("%%a") do (
rem If such a folder exists...
if defined part[%%b] (
rem Get the number of the corresponding array element and move the file
for %%n in (!part[%%b]!) do ECHO move "%%a" "!folder[%%n]!"
) else (
echo No folder exists for this file: "%%a"
)
)
)
This method have also several advantages: you may check if a certain folder does not exists, or get the number of files moved to each folder, etc. If you are not interested in these points, just remove the if command and make the code simpler...
An explanation of array management in Batch files is given at this answer.

Batch programing to search some file in sub folders

I have hundreds of csv files . csv files are stored in folders and sub ​​folders . I want to search fifty csv file whose file names have been determined , for example 1.csv , 2.csv , 3.csv , ... , 50.csv . very troublesome if I searched one by one using the Windows search tool . I would like if the files are found , save in the folder named FOUND . please help to overcome this problem by using the batch programming / bat ? thank you very much
There's a number of approaches one can take, depending on how much automation you require... To help you get started, you may want to look at this it helped me (and indeed continues to do so) when I started learning batch. Furthermore I will provide one possible template for achieving your objective, as I have interpreted it. Perhaps it is not the most elegant or efficient method, but it introduces a number of batch commands that you may or may not have encountered, which in turn may help you develop your own method.
#echo off
setlocal enabledelayedexpansion
echo Please enter a drive letter:
set /p "drive=>"
echo Please enter a search string:
set /p "searchstring=>"
echo %searchstring%>search.txt
set /p search=<search.txt
set /a suffix=0
echo.>>search.txt
:LOOP
for /f "tokens=*" %%i in ("search.txt") do (
set /a suffix=suffix+1
set seq=%search% !suffix!
echo !seq!>>search.txt
)
if !suffix! leq 49 goto LOOP
for /f "tokens=*" %%i in (search.txt) do (
for /f "tokens=*" %%j in ('dir /b /s /a-d %drive%:\"%%i.csv" 2^>nul') do (
if not exist "%~dp0\found" md "%~dp0\found"
move /y "%%j" "%~dp0\found\%%~nxj"
)
)
pause
This is not intended as a definitive solution, though you may find it answers your original query/request. All the best.
Here's another working solution for you..
#ECHO OFF
SETLOCAL EnableDelayedExpansion
REM First Set your directories input and output
SET InputDir=C:\Directory to your CSV files\
SET OutputDir=C:\Directory to your CSV files\FOUND
REM check if the FOUND directory exist, if not, then create it.
IF NOT EXIST OutputDir (
mkdir %OutputDir%
)
REM Grab a scan of the input directory and save it to a temporary file list.
Dir /a /b %InputDir%>"%OutputDir%\Found.txt"
REM Set the files you would like to find.
SET "File1=1.csv"
SET "File2=2.csv"
SET "File3=50.csv"
REM The loop, to process the matching file(s).
FOR %%A IN (%File1%,%File2%,%File3%) DO (
FOR /F "usebackq" %%B IN ("%OutputDir%\Found.txt") DO (
IF %%A==%%B (
copy "%InputDir%\%%A" "%OutputDir%\%%A"
)
)
)
REM Clean up the temp file list.
DEL "%OutputDir%\Found.txt"
Make note, I didn't add quotes to the Input and Output variables, but instead added quotes to the copy portion of the code to compensate for white spaces in your directory path. I tried to keep it simple, so you could follow the logic of how it processed what you are looking for, you can now modify this to your liking.. Have fun. Cheers!

Batch script that moves each file in a folder into their own folders named after the file?

So if I have
/folder/file1.txt
/folder/file2.jpg
/folder/file3.py
I want to create
/folder/file1/file1.txt
/folder/file2/file2.jpg
/folder/file3/file3.py
I have this batch file (be careful where you run it), which mostly works but if there is whitespace in the file name, the folder name will only be named up until the whitespace and so the file won't be moved inside of it.
Also, I only got it to work by arbitrarily putting the word "Folder" or some random string at the end of the folder name, if I exclude that, for some reason it won't work. I'm on windows 7.
#echo off
for /f %%a in ('dir /a-d /b') do (
if not "%%~dpnxa"=="%~dpnx0" call :func "%%~a"
)
goto :EOF
:func
set file=%~1
set dir=%file% Folder
md "%dir%" Folder 2>nul
move "%file%" "%dir%"
goto :EOF
Any ideas on how to address the whitespace/name issues? Thanks in advance.
#echo off
for /f "usebackq delims=?" %%a in (`dir /a-d /b`) do (
if not "%%~dpnxa"=="%~dpnx0" call :func "%%~a"
)
goto :EOF
:func
set file=%~1
set dir=%file% Folder
md "%dir%" Folder 2>nul
move "%file%" "%dir%"
goto :EOF
By setting the delims=? you are saying that your delimiter is a ? to split up a string, instead of the whitespace character, which allows you to read full file names with spaces in them. Usebackq means that you instead use ` around the command to be ran, which to me, just makes it more logical to read and understand "Hey, I'm actually executing this string."
To avoid problems with spaces in paths/file names, double quote all the references to them.
The reason for having to include a string at the end of the folder in your code is you are trying to create a folder with exactly the same name that the file (in your code, you are not removing the extension), and you can not have two elements (files or folders) inside a folder with the same name.
#echo off
for %%a in ("c:\folder\*") do (
if not "%%~fa"=="%~f0" (
if not exist "%%~dpna\" echo md "%%~dpna"
if exist "%%~dpna\" echo move /y "%%~fa" "%%~dpna"
)
)
For each file in the indicated folder
if the file is not the batch file
if not exist a folder with the same name that the file, create it
if the target folder exist, move the file to the folder
%%~fa = full path of the file being processed
%~f0 = full path of the batch file
%%~dpna = drive, path, and file name without extension of the current file being proccesed
In this code, the reason for the third if is to check if the possible previous folder creation has failed. If you have a file with no extension, you will not be able to create the folder, as it will have exactly the same name as the file and this is not allowed.
Code includes echo commands before md and move to show what will be executed. If the output is correct, remove the echo to make it work.

Batch Script to create folder based on name, add to existing code

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.

Resources