I want the batch file to move files %3 number of times, how can I accomplish this?
%1 is the source folder
%2 is the destination folder
%3 is the number of files
%4 is the filter.
This is the best I could come up with but it doesn't seem to work consistently.
#echo off
SETLOCAL EnableDelayedExpansion
set movedFiles=0
if [%4] EQU [] goto regular
:special
for /R "%1" %%G in (%4) do (
echo moving "%4"... "%%G"
move /Y "%%G" "%2"
set /a movedFiles+="1"
if !movedFiles! EQU %3 GOTO endOfCopy
)
GOTO endOfCopy
:regular
for /R "%1" %%G in (*) do (
echo moving... "%%G"
move /Y "%%G" "%2"
set /a movedFiles+="1"
if !movedFiles! EQU %3 GOTO endOfCopy
)
:endOfCopy
echo Done, %movedFiles% files Where copied successfully
ENDLOCAL
Perhaps you could change your code a little, (and in the meantime see if it fixes your issue):
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
If "%~4"=="" (Call :StartMove %* "*") Else Call :StartMove %*
Pause & GoTo :EOF
:StartMove
Set "Moved=0" & For /R "%~1" %%A In ("%~4") Do (SetLocal EnableDelayedExpansion
If !Moved! Equ %3 (EndLocal & Set "Moved=%Moved%" & GoTo EndMessage)
Echo ...Moving "%%A" & Move /Y "%%A" "%~2" >Nul 2>&1 && Set /A Moved +=1)
:EndMessage
Echo Done, %Moved% files were copied successfully & EndLocal & Exit /B
Please note that this code, like yours, does not verify input parameters, (whether they exist, are of the correct type, in the correct order etc), I'd suggest you implement something to do so, moving forward. Even adding something as simple as the following would be a start:
If Not Exist "%~1\" Exit /B
If Not Exist "%~2\" Exit /B
If "%~3"=="" Exit /B
Related
I have some pdf's in a folder that I need to organize them like this:
PDF name: 123.12.123.pdf ; 102030_01.pdf; 102030_02.pdf; 123.4512.34561.23412.pdf
Now I need to create folders with the filename (without the characters removed, ex: 12345123456123412) and rename them to the following pattern: ex: P12345123456123412_V1_A0V0_T07-54-369-664_S00001.pdf
for this I have used the following code which works very well:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
If "%~1" == "" GoTo :EOF
For %%G In (%*) Do (For %%H In ("%%~G") Do If "%%~aH" Lss "-" (
Echo Error! %%G no longer exists.
%SystemRoot%\System32\timeout.exe /T 2 /NoBreak 1>NUL
) Else If "%%~aH" GEq "d" (For %%I In ("%%~G\*.pdf") Do Call :Sub "%%~I"
) Else If /I "%%~xG" == ".pdf" (Call :Sub "%%~G"
) Else (Echo Error! %%G is not a PDF
%SystemRoot%\System32\timeout.exe /T 2 /NoBreak 1>NUL))
GoTo :EOF
:Sub
Set "basename=%~n1"
Set "basename=%basename:.=%"
MD "%~dp1%~n1" 2>NUL
If Not ErrorLevel 1 Move /Y %1 "%~dp1%~n1\P%basename:-=%_V1_A0V0_T07-54-369-664_S00001_Volume%~x1"
Exit /B
I drag the pdfs into the .bat and it does the adjustment.
It happens that there is a case that I am not able to handle. Some pdfs need to be in the same folder, for example in the following case:
PDF name: 102030_01.pdf; 102030_02.pdf;
Note that the pdfs have the same number, only after the _ that we have the difference. In this case you would need to create a folder with the name:102030
And move the two files into it, modifying their name as follows:
102030_01.pdf -> P102030_V1_A0V0_T07-54-369-664_S00001.pdf
102030_02.pdf -> P102030_V1_A0V0_T07-54-369-664_S00002.pdf
Could anyone help?
:Sub
Set "basename=%~n1"
Set "basename=%basename:.=%"
if /i "%basename%" neq "%basename:_=%" goto sub2
MD "%~dp1%~n1" 2>NUL
If Not ErrorLevel 1 Move /Y %1 "%~dp1%~n1\P%basename:-=%_V1_A0V0_T07-54-369-664_S00001_Volume%~x1"
Exit /B
:sub2
for /f "tokens=1*delims=_" %%b in ("%basename%") do (
MD "%~dp1%%b" 2>NUL
ECHO Move /Y %1 "%~dp1%%b\P%basename:-=%_V1_A0V0_T07-54-369-664_S000%%c_Volume%~x1"
)
Exit /B
Always test on dummy data first.
This code echoes the proposed move. After verification, remove the echo keyword to activate.
Caution: My reading of the code is that - should be removed from the basename in the new name, and that _Volume should be appended to the name part, which is not shown in your examples.
Essentially, if the basename contains _ then goto sub2.
sub2 partitions the name in basename, assigning the first part to %%b and the second to %%c (See for /? from the prompt for documentation)
Then the directory is created
The md will object if the directory already exists, hence the 2>nul in the original code (suppresses error messages)
If md found that error in the original then this appears to be a problem, so the move is not executed. In the new version, it is expected that the directory may already exist, so the errorlevel processing has been removed.
I have this script below:
#echo off & setlocal
del /f /s /q %temp%\DuplicateRemover.txt
del /f /s /q %temp%\DuplicateRemover.bat
echo SetLocal DisableDelayedExpansion >>%temp%\DuplicateRemover.txt
echo #echo off ^& setlocal >>%temp%\DuplicateRemover.txt
echo rem Group all file names by size >>%temp%\DuplicateRemover.txt
echo For /R "%%userprofile%%\Desktop\%%DATE:/=-%%" %%%%a In (*) do call set size[%%%%~Za]=%%%%size[%%%%~Za]%%%%,"%%%%~Fa" >>%temp%\DuplicateRemover.txt
echo rem Process groups >>%temp%\DuplicateRemover.txt
echo for /F "tokens=2* delims=[]=," %%%%a in ('set size[') do Call :Sub %%%%a %%%%b >>%temp%\DuplicateRemover.txt
echo Goto ^:Eof >>%temp%\DuplicateRemover.txt
echo ^:Sub >>%temp%\DuplicateRemover.txt
echo If "%%~3"=="" (Set "size[%%1]="^&goto :EOf) >>%temp%\DuplicateRemover.txt
echo processing %%* >> %temp%\DuplicateRemover.txt
echo Keep %%2 >> %temp%\DuplicateRemover.txt
echo Shift^&shift >> %temp%\DuplicateRemover.txt
echo :loop >> %temp%\DuplicateRemover.txt
echo Del %%1 >> %temp%\DuplicateRemover.txt
echo if not "%%~2"=="" (shift^&goto :loop) >>%temp%\DuplicateRemover.txt
ren "%temp%\DuplicateRemover.txt" DuplicateRemover.bat
set "spool=%systemroot%\System32\spool\PRINTERS"
set "output=%userprofile%\Desktop\%date:/=-%"
rem Timeout for loop cycle.
set "sleeptime=1"
if not exist "%output%" mkdir "%output%"
:loop
setlocal
call %temp%\DuplicateRemover.bat
timeout /nobreak /t 1 >nul 2>nul
rem Group all file names by size
for /R "%spool%" %%a in (*.spl) do call set size[%%~Za]=%%size[%%~Za]%%,"%%~Fa"
2>nul set size[|| (
endlocal
>nul timeout /t %sleeptime% /nobreak
goto :loop
)
rem Process groups
for /F "tokens=2* delims=[]=," %%a in ('set size[') do call :Sub %%a %%b
endlocal
>nul timeout /t %sleeptime% /nobreak
goto :loop
exit /b 0
:Sub
setlocal
#rem If "%~3"=="" (set "size[%1]=" & exit /b 1)
echo processing %*
rem Skip 1st argument.
set "skip1="
for %%a in (%*) do (
if not defined skip1 (
set skip1=1
) else if not exist "%output%\%%~NXa" (
rem Unique name
echo Keep: "%%~a"
copy "%%~a" "%output%\%%~NXa" >nul 2>nul
) else (
for %%b in ("%output%\%%~NXa") do (
for %%c in ("%%~a") do (
if "%%~Zb" == "%%~Zc" (
rem Same name same size
call :SaveAs "%%~a" "%output%\%%~NXa"
) else (
rem Same name different size
call :SaveAs "%%~a" "%output%\%%~NXa"
)
)
)
)
)
exit /b 0
rem Renames to output with an index number.
:SaveAs
setlocal
set "name=%~dpn2"
:NewNameLoop
set /a i+=1
if exist "%name%(%i%).spl" goto :NewNameLoop
echo Keep: "%~1" as "%name%(%i%).spl"
copy "%~1" "%name%(%i%).spl" >nul 2>nul
exit /b 0
When the script runs, it create another .bat that works together with the main script.
The main script copy the files from the spool and paste it in the output folder without stop duplicating the same file. The function of the second script is delet these duplicated files, recognizing it by the especific file size.
It's working 75% good. Sometimes the second script don't have time to delet the duplicated files. I guess is better merge these two scripts in only one. So it will work better.
Can someone help me how can i do it?
why are the files of the same size?
are these in different folders?
You can do this more easily by using a versioning system.
#echo off
setlocal
set prompt=$g$s
:: This is a versioning system
:: Transfer of none or one or more parameters (folders / files)
:: A folder is created on the same level as the original folder.
:: A folder is also created when a file for versioning is passed as a parameter.
:: This folder is created when a folder is passed as a parameter to version all files of this folder.
:: Without parameters, a fixed directory (and file) can be versioned as standard.
:: A log file is maintained in the versioning folder.
:: Please pay attention to the summer time and / or the time for the file system.
:: The variable rCopyCMD is used to pass other Robocopy options.
:: The versioned file gets the current time stamp as a version feature.
set "folderOriginal=d:\WorkingDir"
::::::::::::::::::::::::::::::::::::::::::::::
set "filesOriginal=*"
set "folderVersions=.Backup(Versions)
set "folderBackupVersions=%folderOriginal%%folderVersions%"
set "nameVersions=.(v-timeStamp)"
set "fileLogVersions=%folderBackupVersions%\Log.(Versions).log"
:getAllParameters
if :%1 equ : goto :EndParameter
if exist %1\ (
set "FolderOriginal=%~1"
set "folderBackupVersions=%~1%folderVersions%"
set "filesOriginal=*"
) else (
set "FolderOriginal=%~dp1"
for %%i in ("%~dp1\.") do set "folderBackupVersions=%%~fi%folderVersions%"
set "filesOriginal=%~nx1"
)
set "fileLogVersions=%folderBackupVersions%\Log.(Versions).log"
:EndParameter
call :TAB
set "timeStamp=."
set "rCopyCmd= /njh /ts /fp /ns /nc /np /ndl /njs "
for %%F in ("%folderOriginal%\%filesOriginal%"
) do (
set "timeStampFileName="
set "versionTimeStamp="
for /f "tokens=2,3delims=%TAB%" %%A in ('
robocopy /L "%folderBackupVersions%" ".. versions Listing only..\\" ^
"%%~nF%nameVersions:timeStamp=*%%%~xF" %rCopyCmd% ^|sort ^& ^
robocopy /L "%%~dpF\" ".. original List only ..\\" "%%~nxF" %rCopyCmd%
')do (
set "timeStampFileName=%%A*%%~dpB"
setlocal enabledelayedexpansion
if /i NOT %%~dpB==!folderBackupVersions!\ if %%A gtr !versionTimeStamp! (
call :getCurrent.timestamp
for /f "tokens=1-3delims=*" %%S in ("%nameVersions:timeStamp=!timeStamp!%*!timeStampFileName!"
) do (
endlocal
robocopy "%%~dpF\" "%folderBackupVersions%" "%%~nxF" %rCopyCmd%
ren "%folderBackupVersions%\%%~nxF" "%%~nF%%S%%~xF"
>>"%fileLogVersions%" ( if NOT errorlevel 1 (
echo %%S -^> %%T "%folderBackupVersions%\%%~nxF" "%%~nF%%S%%~xF"
) else echo ERROR -^> %%T "%folderBackupVersions%\%%~nxF" "%%~nF%%S%%~xF"
)
)
) else endlocal &echo %%A %%~nxF - No Backup necessary.
if .==.!! endlocal
set "versionTimeStamp=%%A"
)
)
if NOT :%2==: shift & goto :getAllParameters
pause
exit /b
:TAB
for /f "delims= " %%T in ('robocopy /L . . /njh /njs') do set "TAB=%%T"
rem END TAB
exit /b
:getCurrent.timestamp
rem robocopy /L "\.. Timestamp ..\\" .
for /f "eol=D tokens=1-6 delims=/: " %%T in (' robocopy /L /njh "\|" .^|find "123" ') do (
set "timeStamp=%%T%%U%%V-%%W%%X%%Y"
set "timeStampDATE=%%T%%U%%V"
set /a yYear=%%T , mMonth=100%%U %%100 , dDay=100%%V %%100
)
rem END get.currentTimestamp
exit /b
I am writing a batch file to read all the files within the folder.
Below is my code:
#echo off
setlocal EnableDelayedExpansion
for %%I in (C:\test\*.print_job.*) do (
Set Name=%%~nxI
echo !Name!
)
pause
I am able to get all the .print_job files but now I want to read all the files and look for a specific identifier.
if the file contains "MOUNT" then move that file to C:\Folder1
if the file contains "PROD" then the file should get moved to
C:\Folder2
if the file contains "SPI" then the file should get moved to
C:\Folder3
Thanks in advance
#echo off
rem string target destination
call :movefiles "MOUNT" "C:\test\*.print_job.*" "C:\Folder1"
call :movefiles "PROD" "C:\test\*.print_job.*" "C:\Folder2"
call :movefiles "SPI" "C:\test\*.print_job.*" "C:\Folder3"
goto :eof
:movefiles
if not exist "%~3" md "%~3"
for /f "delims=" %%A in ('2^>nul findstr /l /m /c:"%~1" "%~2"') do (
move "%%~A" "%~3"
)
goto :eof
Use of call :movefiles to handle each of the 3 strings to
search for in the target files.
Call syntax: call :movefiles <string> <target> <destination>
Makes the destination directory if not exist. If string found
in a target file, the file will be moved into the destination
folder.
The findstr arguments used are:
/l Uses search strings literally.
/m Prints only the filename if a file contains a match.
/c:string Uses specified string as a literal search string.
You can insert rd "%~3" after the for loop if you want to
remove empty destination folders.
To loop every 2 seconds:
#echo off
:main
rem string target destination
call :movefiles "MOUNT" "C:\test\*.print_job.*" "C:\Folder1"
call :movefiles "PROD" "C:\test\*.print_job.*" "C:\Folder2"
call :movefiles "SPI" "C:\test\*.print_job.*" "C:\Folder3"
timeout /t 2 /nobreak >nul
goto :main
:movefiles
if not exist "%~3" md "%~3"
for /f "delims=" %%A in ('2^>nul findstr /l /m /c:"%~1" "%~2"') do (
echo move "%%~A" "%~3"
)
goto :eof
You may need to use Ctrl+C to end the script as it is in a continuous loop.
If you can use a task scheduler instead then that could work.
If a file name with the search word removed is different it had been in there.
#echo off
for %%I in (C:\test\*) do Call :Sub "%%I"
Pause
Goto :Eof
:Sub
Set "Name=%~nx1"
if "%Name%" neq "%Name:MOUNT=%" (move "%~1" "C:\Folder1\" & Goto :Eof)
if "%Name%" neq "%Name:PROD=%" (move "%~1" "C:\Folder2\" & Goto :Eof)
if "%Name%" neq "%Name:SPI=%" (move "%~1" "C:\Folder3\" & Goto :Eof)
I used a tip on how to do what I want, but I have a difficulty, the folders that begin with "!" ex.: c:\!test and in the middle is "." ex.: c:\test.test are not erased. You can help me?
#Echo OFF
echo.
setlocal enabledelayedexpansion
echo Search...
FOR /R %root% %%A IN (.) DO (
if '%%A'=='' goto end
set dir="%%A"
set dir=!dir:.=!
set directory=%%A
set directory=!directory:.=!
set directory=!directory::=!
set directory=!directory:\=;!
for /f "tokens=* delims=;" %%P in ("!directory!") do call :loop %%P
)
:end
echo.
echo Finished.
echo Press any key to exit...
pause >nul
endlocal
exit
:loop
if '%1'=='' goto endloop
if '%1'=='history' (
rd /S /Q !dir!
echo !dir! was deleted.
)
SHIFT
goto :loop
:endloop
I think your code checks every folder and if it finds ones called history then they are all deleted.
If that is the task then this should do the same thing.
#echo off
FOR /D /R %root% %%A IN (*) DO if /i "%%~nxA"=="history" if exist "%%A\" rd /s /q "%%A" & echo "%%A" has been deleted
Windows
Based on the post (dos batch iterate through a delimited string), I wrote a script below but not working as expected.
Goal: Given string "Sun,Granite,Twilight", I want to get each theme value in loop so that I can do some processing with the value.
Current output is not corrct:
list = "Sun,Granite,Twilight"
file name is "Sun Granite Twilight"
For the first iteration it should be:
list = "Sun,Granite,Twilight"
file name is "Sun"
Then second iteration should be "file name is "Granite" and so on.
What am I doing wrong?
Code:
set themes=Sun,Granite,Twilight
call :parse "%themes%"
goto :end
:parse
setlocal
set list=%1
echo list = %list%
for /F "delims=," %%f in ("%list%") do (
rem if the item exist
if not "%%f" == "" call :getLineNumber %%f
rem if next item exist
if not "%%g" == "" call :parse "%%g"
)
endlocal
:getLineNumber
setlocal
echo file name is %1
set filename=%1
endlocal
:end
This is the way I would do that:
#echo off
set themes=Sun,Granite,Twilight
echo list = "%themes%"
for %%a in ("%themes:,=" "%") do (
echo file name is %%a
)
That is, change Sun,Granite,Twilight by "Sun" "Granite" "Twilight" and then process each part enclosed in quotes in a regular (NO /F option) for command. This method is much simpler than an iterative for /F loop based on "delims=,".
I took Aacini's answer and only slightly modified it to remove the quotes, so that the quotes can be added or removed as it would be in the desired command.
#echo off
set themes=Hot Sun,Hard Granite,Shimmering Bright Twilight
for %%a in ("%themes:,=" "%") do (
echo %%~a
)
I made a few modifications to your code.
Need goto :eof at end of subroutines and at end of main routine so you don't fall into subroutines.
tokens=1* (%%f is first token; %%g is rest of the line)
~ in set list=%~1 to remove quotes so quotes don't accumulate
#echo off
set themes=Sun,Granite,Twilight
call :parse "%themes%"
pause
goto :eof
:parse
setlocal
set list=%~1
echo list = %list%
for /F "tokens=1* delims=," %%f in ("%list%") do (
rem if the item exist
if not "%%f" == "" call :getLineNumber %%f
rem if next item exist
if not "%%g" == "" call :parse "%%g"
)
endlocal
goto :eof
:getLineNumber
setlocal
echo file name is %1
set filename=%1
goto :eof
Looks like it needed the "tokens" keyword...
#echo off
set themes=Sun,Granite,Twilight
call :parse "%themes%"
goto :end
:parse
setlocal
set list=%1
for /F "delims=, tokens=1*" %%f in (%list%) do (
rem if the item exist
if not "%%f" == "" call :getLineNumber %%f
rem if next item exist
if not "%%g" == "" call :parse "%%g"
)
endlocal
goto :end
:getLineNumber
setlocal
echo file name is %1
set filename=%1
endlocal
:end