How to add recursive directory to batch file - batch-file

I have the following batch file:
#echo off
for /f "delims=" %%F in (
'dir /b /a-d [*]*'
) do for /f "tokens=1* delims=]" %%A in (
"%%F"
) do for /f "tokens=*" %%C in ("%%B") do ren "%%F" "%%C"
I want launch it in the root directory and have it go through all directories and subdirectories performing the actions.
I tried adding /D and /r to the 'for' lines, but it doesn't appear to be working.
Do I need add something like...
for /D /r do
under the #echo off ?

Use either dir or for to get all the files, don't mix it up.
When using dir /S for recursive enumeration, regard that full paths are output rather than pure file names only.
This should do it:
#echo off
for /f "delims=" %%F in (
'dir /s /b /a-d [*]*'
) do for /f "tokens=2* delims=]" %%B in (
"%%~nxF"
) do for /f "tokens=*" %%C in ("%%B") do ren "%%~F" "%%C"
So I just changed the following in your original code:
added /s to dir (returns full paths then);
improved second for options (you never used the first token %%A, so why extract it then?);
replaced set %%F of second for by %%~nxF to just parse the file name (type for /? for details concerning substitution modifiers such as ~n, ~x);
replaced source argument "%%F" of ren command by "%%~F" to not fall into double-double-quote problems (the ~ modifier removes potential double-quotes);

You are using "dir" for the enumeration of files, so add "/s" to the DIR command.
I might refactor what you have like this to make it easier to manage.
This also does recursion.
call :TOP .
goto :EOF
:TOP
setlocal
cd "%~f1"
for /f "delims=" %%F in ('dir /b /a-d [*]*') do call :SubRoutine "%%F"
for /D %%x in (*) do call :TOP "%%x" || (echo FAILED2 "%%x" && exit /b 2)
goto :EOF
:SubRoutine
for /f "tokens=1* delims=]" %%A in ("%~1") do call :SubRoutine2 "%~1" "%%A" "%%B"
goto :EOF
:SubRoutine2
for /f "tokens=*" %%C in ("%~3") do ren "%~1" "%%C"
goto :EOF

Related

Windows CMD for matching files to directories

I have a Windows CMD script that has a bug. The script is supposed to match the first 8 digits (the date) of a file with a directory titled with the same first 8 digits (the date). If successful, the file is moved into that directory & placed in a subfolder (called 'portfolio'). However, the error File not Found is returned.
file: 20230202_example.jpg
directory: 20230202_winter-holiday/portfolio
...the CMD file:
#echo off
for /f "delims=" %%a in ('dir /b /a-d') do (
set "filename=%%a"
set "first8=!filename:~0,8!"
for /f "delims=" %%b in ('dir /b /a-d *%first8%*') do (
if /i "!filename!" neq "%%b" (
move "!filename!" "%%b\portfolio\!filename!"
)
)
)
If I interrogate the directory in Command Prompt:
dir /b /a-d
...I get a full list of the files contained. When the script is run from Command Prompt, for each file contained I get:
File Not Found
Based upon your target file and directory names, you could probably do it without defining variables and therefore the need to delay variable expansion.
Example:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Delims=" %%G In ('Dir "????????_*" /A:-D /B 2^>NUL
^| %SystemRoot%\System32\findstr.exe /R /C:"^202[0123]1[012][12][0123456789]_."
/C:"^19[789][0123456789]0[123456789]0[123456789]_."
/C:"^19[789][0123456789]0[123456789][12][0123456789]_."
/C:"^19[789][0123456789]0[123456789]3[01]_." /C:"^202[0123]0[123456789]3[01]_."
/C:"^19[789][0123456789]1[012]0[123456789]_."
/C:"^19[789][0123456789]1[012][12][0123456789]_."
/C:"^19[789][0123456789]1[012]3[01]_." /C:"^20[01][0123456789]1[012]3[01]_."
/C:"^20[01][0123456789]0[123456789]0[123456789]_." /C:"^202[0123]1[012]3[01]_."
/C:"^20[01][0123456789]0[123456789][12][0123456789]_."
/C:"^20[01][0123456789]0[123456789]3[01]_."
/C:"^20[01][0123456789]1[012]0[123456789]_."
/C:"^20[01][0123456789]1[012][12][0123456789]_."
/C:"^202[0123]0[123456789]0[123456789]_." /C:"^202[0123]1[012]0[123456789]_."
/C:"^202[0123]0[123456789][12][0123456789]_."
') Do For /F "Delims=_" %%H In ("%%~nG") Do For /F "Delims=" %%I In ('
Dir "%%H_*" /A:D /B 2^>NUL'
) Do %SystemRoot%\System32\Robocopy.exe . "%%I\portfolio" "%%G" /Mov 1>NUL 2>&1
I decided to use a little more accuracy in the dates, (it's not perfect because it has no knowlege of how many days are in each month of any year, but should cover dates between 19700101 and 20231231). If you don't want that, you could simplify it by just removing lines 4 through 17:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Delims=" %%G In ('Dir "????????_*" /A:-D /B 2^>NUL
') Do For /F "Delims=_" %%H In ("%%~nG") Do For /F "Delims=" %%I In ('
Dir "%%H_*" /A:D /B 2^>NUL'
) Do %SystemRoot%\System32\Robocopy.exe . "%%I\portfolio" "%%G" /Mov 1>NUL 2>&1

How do you recursively delete a named directory from all volumes in Batch?

echo off
for /d /r "c:\" %%a in (TemporaryFolder) do if exist "%%a" echo Removing %%a & rmdir /s /q "%%a"
This for loop deletes every directory called "TemporaryFolder" from the C:\ drive. How would I go through every mounted volume (ie. A:\ - Z:) to delete the directory "TemporaryFolder"?
Edit (This test didn't work):
#echo off
echo Deleting Temporary Folders... Please be patient.
For /F "Tokens=*" %%A In ('MountVol^|Find ":\"') Do For /F "Delims=" %%B In ('Dir /B/S/AD-S-L "%%ATemporaryFolder" 2^>Nul') Do RD /S/Q "%%B" 2>Nul & echo Deleting %%B
echo Successfully deleted.
pause
Based upon my comment, here's an example of a nested For loop, which should do as asked:
For /F "Tokens=*" %%A In ('MountVol^|Find ":\"') Do For /F "Delims=" %%B In ('Dir /B/S/AD-S-L "%%ATemporaryFolder" 2^>Nul') Do RD /S/Q "%%B" 2>Nul
The outer For loop runs the MountVol command which returns the mounted drive paths as %%A. The nested For loop performs the recursive directory search for those named TemporaryFolder located within %%A.
Edit
Here's a multiline version of the same routine with added messages:
#Echo Off
Set "objFolder=TemporaryFolder"
Echo Please be patient...
For /F "Tokens=*" %%A In ('MountVol^|Find ":\"') Do (
Echo Deleting directories named %objFolder% from %%A
For /F "Delims=" %%B In ('Dir /B/S/AD-S-L "%%A%objFolder%" 2^>Nul') Do (
Echo Deleting %%B
RD /S/Q "%%B" 2>Nul && Echo Successfully deleted %%B
)
)
Pause
You should only modify the objFolder name on line 2.
Instead of MountVol, this one uses PowerShell to get the drives, and matches System and Reparse Points too, (take account that you'd need appropriate permissions to remove system directories):
#Echo Off
Set "objFolder=TemporaryFolder"
Echo Please be patient...
For /F "Tokens=*" %%A In (
'PowerShell -NoP "GDr -P FileSystem|?{!$_.Used -Eq ''}|Select -Exp Root"'
) Do (Echo Deleting directories named %objFolder% from %%A
For /F "Delims=" %%B In ('Dir /B/S/AD "%%A%objFolder%" 2^>Nul') Do (
Echo Deleting %%B
RD /S/Q "%%B" 2>Nul && Echo Successfully deleted %%B
)
)
Pause

batch file remove X characters of filename

I did batch file which copy 3 files and need to rename it by removing last 33 characters. The copy works fine but removing last 33 characters not... I saw more then one answer on web and try it all but nothing work so far.
My batch file look like this:
for /f "delims=" %%i in ("my folder") do (
ren "%%i" "%i:~0,-33%".txt
)
I tried already:
set fName=%%i
ren "%fName%" "%fName:~0,-33%.txt"
From the information I got here, try this:
#echo off
setlocal enabledelayedexpansion
set "folderpath=[Your Folder Here...]"
cd %folderpath%
for /f %%a in ('dir /b "*.txt"') do (
set "fname=%%~na"
ren "%%a" "!fname:~0,-33!.txt"
)
endlocal
This is similar to the answer above. You should make sure the batch file is OUTSIDE the folder.
EDIT.
When dealing with variables formed inside FOR and IF's, use delayed expansion (i.e. !var!, instead of %var%). Anyway, this is the fixed code:
#echo off
setlocal enabledelayedexpansion
::NO Last Backslash...
set "sourcepath=C:\Users\tzahi.k\Desktop\testSource\source2"
set "folderpath=C:\Users\tzahi.k\Desktop\testSource\des"
for /F "delims=" %%a in ('dir /b /od "%sourcepath%\*.txt"') do (
set "youngest=%%a"
xcopy /y "%sourcepath%\!youngest!" "%folderpath%"
)
cd /d %folderpath%
for /f %%a in ('dir /b "*.txt"') do (
set "fname=%%~na"
ren "%%a" "!fname:~0,-33!.txt"
)
endlocal
pause
Here's the batch file you'd want to run:
#echo off
Setlocal EnableDelayedExpansion
#for /f "delims=" %%i in ('dir /b *.txt') do (
set fname=%%~ni
set fname=!fname:~0,-33!.txt
ren "%%i" "!fname!"
)
endlocal
This should work
#echo off
setlocal enabledelayedexpansion
set FOLDER_PATH=C:\Some\Path\
for %%f in (%FOLDER_PATH%*) do if %%f neq %~nx0 (
set "filename=%%~nf"
ren "%%f" "!filename:~0,-33!%%~xf"
)
PAUSE
Or better this
#echo off & setLocal enableDELAYedeXpansion
for /f "tokens=* delims= " %%a in ('dir /b *.txt') do (
set F=%%~Na
set F=!F:~0,33!
move /y "%%a" "!F!%%~Xa"
)

FORFILES in FOR loops issues

i am trying to make a script to remove empty folders and delete files a number of days old. depending on what the txt file delimiters are set to. I have came up with this so far:
::Batch
SET CDID=%~dp0
SET TEST=TRUE
IF %TEST%==TRUE (
SET COMND1=ECHO
SET COMND2=ECHO
) ELSE (
SET COMND1=DEL
SET COMND2=RD
)
ECHO FILE RAN %date:~10%/%date:~4,2%/%date:~7,2% >>%CDID%\LOG.TXT
FOR /F "usebackq delims=| tokens=1,2" %%x IN (%CDID%PATH.txt) DO (
CALL :DEL_FOLDERS "%%x" %%y
CALL :DEL_FILES "%%x" %%y
)
GOTO :EOF
:DEL_FILES
FORFILES /p %1 /s /m *.* /d %2 /c "cmd /c %COMND1% #file"
GOTO :EOF
:DEL_FOLDERS
FOR /f "delims=" %%i in ('dir %%1 /s /b /ad ^| sort /r') do %COMND2% "%%i"
GOTO :EOF
::PATH.txt
C:\Temp\BLANK|10
C:\Temp\New folder|30
when i run the script #file will not populate and %%i will not populate, i am not sure what i am doing wrong. Help?
You made a couple of very small errors. In DEL_FOLDERS you used %%1 which meant that the argument was not expanded (you only needed one % here). You also did not handle the case where there are no files that match or the directories are empty. In the FORFILES command you put /m *.*; although the documentation says this is the default, the documentation is incorrect. Missing out the /m matches all files (the default) but by saying /m *.* you only match files with a dot!
My corrected version is:
::Batch
SET CDID=%~dp0
SET TEST=TRUE
IF %TEST%==TRUE (
SET COMND1=ECHO
SET COMND2=ECHO
) ELSE (
SET COMND1=DEL
SET COMND2=RD
)
ECHO FILE RAN %date:~10%/%date:~4,2%/%date:~7,2% >>%CDID%\LOG.TXT
FOR /F "usebackq delims=| tokens=1,2" %%x IN (%CDID%PATH.txt) DO (
CALL :DEL_FOLDERS "%%x" %%y
CALL :DEL_FILES "%%x" %%y
)
GOTO :EOF
:DEL_FILES
FORFILES /p %1 /s /d %2 /c "cmd /c %COMND1% #file" 2> nul
GOTO :EOF
:DEL_FOLDERS
FOR /f "delims=" %%i in ('dir "%~1" /s /b /ad 2^>nul ^| sort /r') do %COMND2% "%%i"
GOTO :EOF
::PATH.txt
C:\Temp\BLANK|10
C:\Temp\New folder|30

batch file command to compare newest file and output differences

I have the below code which someone gave to me but I don't know how to put it together in a bat file so it runs successfully.
The aim is to find the latest (last modified) file in c:/ and compare it with c:/2.txt and output the differences into c:/786.txt
cd /d c:\
for /f %%a in ('dir /b /o-d /a-d /tw') do (set latest=%%a & goto :eof)
for /f "tokens=1*" %%a in (
'diff c:\%latest% c:\2.txt ^| findstr /r /c:"^<" /c:"^>"'
) do #echo %%b >>c:\786.txt
Can someone please put this code together for me.
cd /d c:\
set "latest="
for /f %%a in ('dir /b /o-d /a-d /tw') do (set "latest=%%a" & goto :found)
:found
if not defined latest exit /b
for /f "tokens=1,*" %%a in (
'diff "c:\%latest%" "c:\2.txt" ^| findstr /r /c:"^<" /c:"^>"'
) do (
>> "c:\786.txt" echo(%%b
)
Ordering by date descending, the latest file is the first, so on first iteration assign the file name and exit of the for loop.
Then check if any file has been found. It not, end of the script
If we have a file, compare latest file against the indicated one and send the filtered lines to the final file.
EDIT - Refactor code to made it more usable and adapt to comments. Search for last file in folder moved to a subroutine.
#echo off
setlocal enableextensions disabledelayedexpansion
call :getLatestFileInFolder "c:\" latestC
call :getLatestFileInFolder "d:\" latestD
if not defined latestC ( echo NO File in C & exit /b )
if not defined latestD ( echo NO File in D & exit /b )
for /f "tokens=1,*" %%a in (
'diff "%latestC%" "%latestD%" ^| findstr /r /c:"^<" /c:"^>"'
) do (
>> "c:\786.txt" echo(%%b
)
endlocal
exit /b
:getLatestFileInFolder folderToSearch variableToReturn
setlocal
set "folder=%~1" & if not defined folder set "folder=%cd%"
set "latest="
pushd "%folder%"
for /f "tokens=*" %%a in ('dir /b /o-d /a-d /tw 2^>nul') do (set "latest=%%~fa" & goto :latestFileFound)
:latestFileFound
popd
endlocal & set "%~2=%latest%" & goto :eof

Resources