Long time reader, first time poster - appreciate any help you can give.
Context: I am looking to tidy up a set of property folders. Currently the parent folders are the projects and the properties are sub-folders. If a property is affected by multiple projects it will be duplicated across the project folders. My goal is to flatten the structure so I simply have one folder per property. (eg., copy from E:\TESTING\Project\Property*.* to E:\TESTING\Property*.*) (NOTE: I'm looking to use a Windows batch script as I don't have permissions to load any custom software)
Issue: I need to consolidate files from the duplicate folders into a new location and ensure any duplicate files are kept and renamed with a suffix. There are thousands of folders so I will need a looping script to minimize manual effort.
Attempt: I found a great script in the forums (copied below) that handles the duplicate file renaming really well and sets the source from a *.txt file. I can't figure out how to set the variables so I can have custom/looping target folders. (I'm happy to develop a *.txt file that will show the relevant target folder per source file if I need to)
Hoping it's something simple that I just don't know yet.
#echo off
setlocal
set "source=E:\TESTING\source.txt"
set "target=E:\TESTING\Destination3\"
for /f "delims=" %%A in (%source%) do (
if not exist "%target%\%%~nxA" (
copy "%%~A" "%target%\%%~nxA"
) else (
call :index "%%~A" "%target%\%%~nxA" "1"
)
)
exit /b
:index source, target, count
setlocal
set /a "cnt=%~3"
if exist "%target%\%~n2(%cnt%)%~x2" (
call :index "%~1" "%~2" "%cnt%+1"
) else copy "%~1" "%target%\%~n2(%cnt%)%~x2"
pause
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" '
) DO (
rem project in %%a
FOR /f "delims=" %%b IN (
'dir /b /ad "%sourcedir%\%%a\*" '
) DO (
rem property in %%b
ECHO Project\Property being processed : %%a\%%b
rem create new? property directory
MD "%destdir%\%%b" 2>NUL
rem for all files in the property directory,
FOR /f "delims=" %%p IN (
'dir /b /a-d "%sourcedir%\%%a\%%b\*" '
) DO (
IF EXIST "%destdir%\%%b\%%p" (
rem existing filename - need to change
SET "notcopied=Y"
FOR /L %%r IN (1,1,99) DO IF DEFINED notcopied IF NOT EXIST "%destdir%\%%b\%%~np(%%r)%%~xp" COPY "%sourcedir%\%%a\%%b\%%p" "%destdir%\%%b\%%~np(%%r)%%~xp" >nul&SET "notcopied="
) ELSE (
COPY "%sourcedir%\%%a\%%b\%%p" "%destdir%\%%b\%%p" >nul
)
)
)
)
GOTO :EOF
Not really sure why you're reading a file to provide your source directory - or is it directories?
Neither am I sure why you appear to be creating the copies in the same parent - Is there some way of distinguishing a property from a project?
Anyhow, the above routine simply assigns each project directoryname in turn to %%a, and then each project subdirectory to %%b. Then read each filename within %%a\%%b to %%p, create the required subdirectory in the destination (2>nul disposes of the duplicate name messages) and see whether the file already exists in the destination.
If it doesn't, copy it as-is. If it does, then try adding (1)... to the name part until the result is not found. The notcopied flag is used as a switch as if defined is interpreted with the current value of the variable, so setting notcopied to empty makes it not defined, so the loop doesn't try to execute the test further.
OK - the limitation is the final value for %%c - the duplicate-filename counter. I set 99 as the maximum. Adjust as required. The smaller the value you use here, the faster (or more accurately, less slow) the routine is.
Related
I have a batch that scans a folder and puts the files in folders depending of the first letter of each file :
setlocal EnableDelayedExpansion
::Fichiers non déplacés
set noms = gamelist.bmp test.gif truc.pdf
FOR /F "tokens=*" %%A in ('DIR /B /A:-D "%cd%\*"') do IF NOT "%%A"=="%~nx0" ( IF NOT "%%A" in %noms% (
set "FIRSTCHAR=%%~nA"
set "FIRSTCHAR=!FIRSTCHAR:~0,1!"
IF NOT EXIST "%cd%\!FIRSTCHAR!" MD "%cd%\!FIRSTCHAR!"
MOVE "%cd%\%%A" "%cd%\!FIRSTCHAR!"
))
I have two noob questions I gave all I have to make this script lol) :
I created an array named "noms" that I want to use to put filenames that won't be affected by the script (they won't be moved in the folder created by the script). Unfortunately, my "IF NOT "%%A" in %noms%" condition doesn't work. What is wrong please ?
Second question : do you think it's possible to put all the files that begin by a number in a unique folder, "0-9" for instance ?
Thanks for the help !
set "noms=gamelist.bmp test.gif truc.pdf"
FOR /F "tokens=*" %%A in ('DIR /B /A:-D "%cd%\*"') do (
set "mouvement=O"
for %%Q in ("%~nx0" %noms%) do if /i "%%~Q"=="%%~nxA" set "mouvement="
if defined mouvement (
Batch is sensitive to spaces in an ordinary string SET statement. SET FLAG = N sets a variable named "FLAGSpace" to a value of "SpaceN"
mouvement is initialised to a value (any value will do) for each filename, but if the filename matches any of the values in noms or the batch filename %~nx0 then it is set to nothing and becomes not defined.
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.
I have a custom service that automatically generates files every 60 mins into a particular directory with part of the filename incrementing numerically, Eg:
File_00004.job
File_00003.job
File_00002.job
File_00001.job
Currently I have an issue where on occasion a file isn't generated, which results in gaps in the file sequence. This issue then causes a number of issues if not identified ASAP.
I'd like a batch file to identify if I have a gap in the file name sequence.
Tried looking for solutions from existing posts, but haven't found something that fits, so apologies if this has been covered elsewhere.
#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\t w o"
SET "mask=file_??????.job"
SET "lowest="
SET "highest="
FOR /f "delims=" %%a IN (
'dir /b /a-d /on "%sourcedir%\%mask%" '
) DO (
IF NOT DEFINED lowest SET "lowest=%%~na"
SET "highest=%%~na"
)
SET "lowest=%lowest:*_=1%"
SET "highest=%highest:*_=1%"
ECHO checking range %lowest:~1% to %highest:~1%
:: See whether an entry in the range is missing; report&create an empty file if so.
FOR /L %%a IN (%lowest%,1,%highest%) DO SET "name=%%a"&SET "name=file_!name:~1!.job"&IF NOT EXIST "%sourcedir%\!name!" echo !name! missing&(copy nul "%sourcedir%\!name!" >nul)
GOTO :EOF
Alternative structure for the for /L loop:
FOR /L %%a IN (%lowest%,1,%highest%) DO (
SET "name=%%a"
SET "name=file_!name:~1!.job"
IF NOT EXIST "%sourcedir%\!name!" (
echo !name! missing
copy nul "%sourcedir%\!name!" >nul
copy "d:\path to\template.file" "wherever\!name!" >nul
copy "d:\path to\template.file" "anotherplace\!name!" >nul
echo Batch is fun and powerful
copy "d:\path to\template.file" "a third place\!name!" >nul
)
)
The critical point is the positioning of the ( - must be directly after and on the same line as do or else or the logical comparison clause of if and must be matched by a ) (which doesn't need to be on its own line - I find it easier that way, to align indentation.) )s that are not intended to close a block need to be escaped with ^, thus: ^)
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.
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.