I have two folders that contain all the same files and subfolders, but the conents inside each file may have changed. I want to write a batch file that will search through each file and look for any differences. What's the best tool for what I want to do?
No need for a batch file. A single FC command can do what you want:
fc folder1\* folder2\*
You can be more specific for the file mask in the first folder if you want. For example folder1\*.txt.
The command will report on files that exist in folder1 but are missing in folder2. Extra files in folder2 are simply ignored.
There are a number of options to the FC command. Enter HELP FC or FC /? from the command prompt to get more information.
EDIT
Extending the solution to support subfolders is a bit tricky. It is easy to iterate the folder hierarchy for a given root using FOR /R. The problem is getting the relative paths so that the hierarchy can be applied to another root.
The simplest solution is to use FORFILES instead, since it directly supports relative paths. but FORFILES is... S L O W :/
At this point, a batch file makes sense:
#echo off
setlocal
set "folder1=c:\path\To\Folder1\Root"
set "folder2=d:\path\To\Folder2\Root"
set "fileMask=*"
for /f "delims=" %%F in (
'echo "."^&forfiles /s /p "%folder1%" /m "%fileMask%" /c "cmd /c if #isdir==TRUE echo #relpath"'
) do fc "%folder1%\%%~F\%fileMask%" "%folder2%\%%~F\*"
Here is another way to accomplish the task. Set the variables Folder1 and Folder2 to the full path of the folders you want to compare and run the batch file.
Output:
Dup – the file exists in both folders and are identical.
Dif - the file exists in both folders but the content of the files are different.
New – The file exists in one folder but not the other.
#Echo Off
SetLocal EnableDelayedExpansion
Set "Folder1=%UserProfile%\Desktop\Test Folder1"
Set "Folder2=%UserProfile%\Desktop\Test Folder2"
For /R "%Folder1%" %%x In (*.*) Do (
Set "FullPath=%%x"
Set "RelPath=!FullPath:%Folder1%=!"
If Exist "%Folder2%!RelPath!" (
>Nul 2>&1 FC /b "%Folder1%!RelPath!" "%Folder2%!RelPath!" && (
Echo Dup - %%x
)||(
Echo Dif - %%x
)
) Else (
Echo New - %%x
)
)
For /R "%Folder2%" %%x In (*.*) Do (
Set "FullPath=%%x"
Set "RelPath=!FullPath:%Folder2%=!"
If Not Exist "%Folder1%!RelPath!" (
Echo New - %%x
)
)
I'm having better luck with the following batch file. The path names have to completely match though; so only the Drive Letter differs.
Mapping a shared network folder may make this easier. I can't say for sure for your case.
'---
setlocal
set "folder1=D:\User\Public"
set "Drv2=E:"
set "fileMask=*"
setlocal
set "folder1=<DrvLttr>:\<Folder>\<SubFolder>"
set "Drv2=<DrvLttr2>:"
set "LogFile=<SystemDrv>\User\<UserName>\<LogFileName>"
ECHO "the '>' may OVER WRITE or make a ~NEW~ File for the results" > "%LogFile%"
ECHO " '>>' adds to the end of the file >> "%LogFile%"
FOR /R %folder1% %%A in ( *.* ) DO FC "%%A" "%Drv2%%%~pnxA" 1>> "%LogFile%" 2>&1
If you wish to note what is going to be the command for testing, you can try inserting ECHO after DO. You can drop the 1>>... stuff to see the result on screen, instead of having to open the output file.
I modified a batch file I wrote for a CD process that should meet your need. It
takes 2 directory trees and compares each file in each tree
creates a list of the file differences (named File_differences.txt in the output folder)
and creates a folder with a diff file for each non-matching object
If both directory structures are under the same parent, then all you need to do is update the first 8 variables in the script. If the directory trees have different parents, then read through the scripts comments. Particularly lines 14, 38 and 46.
:: This script compares the contents of 2 trees
:: set a workspace location for the script outside of the trees being reviewed
set home=D:\path\to\batch_file_home
set Input=D:\path\to\batch_file_home\Input_Files
set Resource=D:\path\to\batch_file_home\Resource_Files
set Output=D:\path\to\where your want to view your\Output_Files
set environment=D:\path\to\parent directory containing the different tree structures (if they do not share a parent, then make this the drive)
:: the next 3 lines are only needed if you want to predefine multiple directories for comparison
set Prod=production
set QA=test
set Dev=branches\dev
:: If you remove the 3 lines above, then you need to replace the 2 below variables with values
:: If the trees are not under the same parent, then include the full path for Tree A and B below
set Tree_A=%Prod%
set Tree_B=%QA%
:: if you already have an object list, place it in the Input folder and remove lines 24 through 35 of this script
set Object_List=Object_List_minus_Direcotries.txt
set Output_File=File_differences.txt
if exist %Output%\%Output_File% del %Output%\%Output_File%
if exist %Output%\Differences\*.txt del %Output%\Differences\*.txt
if exist %Resource%\* del /q %Resource%\*
:: since you state the objects in both trees are always the same, I have not included a comparison to verify the 2 trees match
:: this section identifies the contents of Tree A
cd %environment%\%Tree_A%
dir /b /s > %Resource%\Complete_Tree_A_Object_List.txt
:: Next, remove the objects that are directories
setlocal enabledelayedexpansion
for /f "tokens=*" %%p in (%Resource%\Complete_Tree_A_Object_List.txt) do (
dir /a:d /b %%p 2>nul >nul && (set object_type=folder) || (set object_type=file)
echo !object_type!
if !object_type!==file echo %%p >> %Resource%\%Object_List%
)
:: in the object list, remove the parent tree from the path
:: if the Trees are not under the same parent, then comment out the below 6 lines
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '\\','/' | set-content %Resource%\%Object_List%"
powershell -command "(get-content %Resource%\%Object_List%) | Foreach {$_.TrimEnd()} | Set-Content %Resource%\%Object_List%"
set remove_parent_prefix=%environment%\%Tree_A%
set remove_parent_prefix=%remove_parent_prefix:\=/%
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '%remove_parent_prefix%/','' | set-content %Resource%\%Object_List%"
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '/','\' | set-content %Resource%\%Object_List%"
:: the below loop assumes both Trees are under the same parent. If this is not the case, replace the cd %environment% line with cd %home%
:: when the Trees are not under the same parent, set home to the root location, example cd D:\
setlocal enabledelayedexpansion
for /f "tokens=*" %%x in (%Resource%\%Object_List%) do (
set Diff_File=%%x
set Diff_File=!Diff_File:\=-!
cd %environment%
fc %Tree_A%\%%x %Tree_B%\%%x > "%Output%\Differences\!Diff_File!-%Output_File%"
for %%a in ("%Output%\Differences\!Diff_File!-%Output_File%") do for /f %%b in ('find /c /v "" ^< "%%a" ') do if %%b LSS 3 del "%%a"
for %%R in ("%Output%\Differences\!Diff_File!-%Output_File%") do if not %%~zR lss 1 (
echo %%x >> %Output%\%Output_File%
)
for %%R in ("%Output%\Differences\!Diff_File!-%Output_File%") do if %%~zR lss 1 (
del "%Output%\Differences\!Diff_File!-%Output_File%"
)
)
endlocal
:: Clean up Resources. If you want to review the temp files used to create the report, comment out the below line
if exist %Resource%\* del /q %Resource%\*
Related
I have a folder named G:\Project\Projects
This folder has some(about 20) unknown subfolders. Some of the subfolders have a folder named SSS and a file called deleteme.doc
I want to delete all these SSS folders(with contents) and deleteme.doc files using a batch file or powershell file. I do not want to delete these files or folder deeply nested into the subfolders, just those who are directly under subfolder.
For example I want to delete this folder
G:\Project\Projects\proj 1\sss
but not these 3-
G:\Project\Projects\proj 1\other\sss
G:\Project\Projects\sss
G:\Project\Projects\proj 1\sss (it is a file)
The thing is I want to simply double click-run the batch/powershell file from anywhere, possibly from another partition or drive (or even network computer).
I tried RMDIR G:\Project\Projects\*\SSS it didn't seem to be working.
Please explain your answer if possible, also show me commands for both recycle bin delete and parma delete. Though I think sending to recycle bin is not possible without powershell.
Administrator privilege is not necessary to delete folders in non-system partition right?
Windows 10. powershell version 5
#ECHO Off
SETLOCAL
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
:: set defaults
SET "$dirname=sss"
SET "$filename=deleteme.doc"
SET "$from=c:\wherever\you\like"
:: analyse command line
:: syntax file="filename" " dir=dirname" from="dirname" (/switches)
:: each is optional. default is as established above
:: /d - delete directory only if it is completely empty
:: /dn - delete directory only if it is not completely empty
:: /ds - delete directory only if it is completely empty or if no files in its subdirectory tree
:: /fe - delete file if empty
:: /fn - delete file if not empty
:: /b - both file and directory must exist to make deletions
::
FOR %%a IN (%*) DO IF DEFINED $twopart (CALL :parttwo %%a) ELSE (
SET "$processed="
IF /i "%%a"=="dir" SET "$processed=Y"&SET "$twopart=$dirname"
IF /i "%%a"=="file" SET "$processed=Y"&SET "$twopart=$filename"
IF /i "%%a"=="from" SET "$processed=Y"&SET "$twopart=$from"
IF /i "%%a"=="/b" CALL :setswitch b
IF /i "%%a"=="/d" CALL :setswitch d
IF /i "%%a"=="/dn" CALL :setswitch dn
IF /i "%%a"=="/ds" CALL :setswitch ds
IF /i "%%a"=="/fe" CALL :setswitch fe
IF /i "%%a"=="/fn" CALL :setswitch fn
IF NOT DEFINED $processed CALL :extras %%a
)
IF DEFINED $extras ECHO(%$extras% NOT understood)&pause&GOTO :EOF
:: resolve switch compatibility
IF DEFINED $d IF DEFINED $dn SET "$incompatible=/d /dn"
IF DEFINED $fe IF DEFINED $fn SET "$incompatible=%$incompatible% /f /fn"
IF DEFINED $incompatible ECHO(%$incompatible% incompatible)&pause&GOTO :EOF
:: if $from is set, make it quoted.
IF DEFINED $from SET "$from="%$from:"=%""
:: Now search for the directory
FOR /d /r %$from% %%a IN ("%$dirname%") DO IF EXIST "%%a\." (
SET "$victim=%%a"
CALL :deldir
)
IF NOT DEFINED $b FOR /r %$from% %%a IN ("%$filename%") DO IF EXIST "%%a" (
SET "$victim=%%~dpa%$filename%"
CALL :delfile
)
GOTO :eof
:: delete the victim directory if all other conditions are met
:: take care of file as well if required.
:deldir
SET "$victim=%$victim:"=%"
IF DEFINED $b IF NOT EXIST "%$victim%\..\%$filename%" GOTO :eof
:: if the directory has any contents and "/d" was specified, skip deletion
IF DEFINED $d (
FOR /f %%z IN ('dir /b "%$victim%" 2^>nul') DO GOTO :eof
)
:: if the directory has files in its subtree and "/ds" was specified, skip deletion
IF DEFINED $ds (
FOR /f %%z IN ('dir /s /b /a-d "%$victim%" 2^>nul') DO GOTO :eof
)
:: if the directory has no files and none in its subtree and "/dn" was specified, don't skip deletion
IF DEFINED $dn (
FOR /f %%z IN ('dir /b "%$victim%" 2^>nul') DO GOTO deldir1
GOTO :eof
)
:: delete the directory
:deldir1
ECHO(DEL /s /f /q "%$victim%"
SET "$victim=%$victim%\..\%$filename%"
IF EXIST "%$victim%" GOTO delfile
GOTO :eof
:delfile
FOR %%z IN ("%$victim%") DO (
IF DEFINED $fn IF "%%~zz"=="0" GOTO :EOF
IF DEFINED $fe IF "%%~zz" neq "0" GOTO :EOF
)
ECHO(DEL /f /q "%$victim%"
GOTO :eof
:parttwo
SET "%$twopart%=%~1"
SET "$twopart="
GOTO :eof
:extras
SET "$extras=%$extras% %~1"
GOTO :eof
:setswitch
SET "$processed=Y"
SET "$%1=Y"
SHIFT
IF "%1" neq "" GOTO setswitch
GOTO :EOF
Here's a general solution which you'd need to test before unleashing.
The required RD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(RD to RD to actually delete the directories.
The required DEL commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(DEL to DEL to actually delete the files.
The first section analyses the parameters ans switches supplied to the program.
The defaults are shown and need to be adjusted to suit your circumstances.
The command may be run as
*thisname* from=c:\new\location dir=xxx file=hello.txt /d
where from specifies where the tree-walk begins, fie the filename to be located and dir the directoryname to delete.
The switches are also listed.
The program simply examines the command line for elements of interest and sets values as required.
We then traverse the tree, looking for the directoryname and then make decisions depending on the switch-settings.
Then traverse the tree again, looking for the filename.
Something like
for /f %A in ('dir "c:\somewhere\deleteme.doc"') Do echo rd "%~dpA"
Administrator privilege is not necessary to delete folders in non-system partition right?
How would we know what permissions you have. Type icacls c:\somewhere to see.
You need to know your problem.
I created a short powershell solution using this-
$targetName='ss s';
foreach ($file in Get-Childitem "G:\Project\Projects\" )
{
$fname=$file.name;
if (Test-Path "G:\Project\Projects\$fname\$targetName\")
{
$shell = new-object -comobject "Shell.Application"
$item = $shell.Namespace(0).ParseName("G:\Project\Projects\$fname\$targetName")
$item.InvokeVerb("delete")
}
}
This powershell script sends that folder to recycle bin after confirmation popup. (this won't send any file named 'ss s')
this seems to be working for batch file script-
set "b=ss s"
for /d %%a in (*) do IF EXIST "%%a\%b%\*" (rmdir /s "%%a\%b%")
if the targetfolder was named "$ss s" then you have to set variables as "b=/$ss s"
I want to create a .bat script to copy only one random file from each folder (also subfolders, so recursively) whilst also keeping the folder structure. I've tried the following code which comes close to what I want but doesn't copy the folder structure and one file per folder.
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
SET Destination=H:\Temp
SET FileFilter=.ape
SET SubDirectories=/S
SET Source=%~dp1
SET FileList1Name=FileList1.%RANDOM%.txt
SET FileList1="%TEMP%\%FileList1Name%"
SET FileList2="%TEMP%\FileList2.%RANDOM%.txt"
ECHO Source: %Source%
IF /I {%SubDirectories%}=={/S} ECHO + Sub-Directories
IF NOT {"%FileFilter%"}=={""} ECHO File Filter: %FileFilter%
ECHO.
ECHO Destination: %Destination%
ECHO.
ECHO.
ECHO Building file list...
CD /D "%Source%"
DIR %FileFilter% /A:-D-H-S /B %SubDirectories% > %FileList1%
FOR /F "tokens=1,2,3 delims=:" %%A IN ('FIND /C ":" %FileList1%') DO SET TotalFiles=%%C
SET TotalFiles=%TotalFiles:~1%
ECHO The source has %TotalFiles% total files.
ECHO Enter the number of random files to copy to the destination.
SET /P FilesToCopy=
ECHO.
IF /I %TotalFiles% LSS %FilesToCopy% SET %FilesToCopy%=%TotalFiles%
SET Destination="%Destination%"
IF NOT EXIST %Destination% MKDIR %Destination%
SET ProgressTitle=Copying Random Files...
FOR /L %%A IN (1,1,%FilesToCopy%) DO (
TITLE %ProgressTitle% %%A / %FilesToCopy%
REM Pick a random file.
SET /A RandomLine=!RANDOM! %% !TotalFiles!
REM Go to the random file's line.
SET Line=0
FOR /F "usebackq tokens=*" %%F IN (%FileList1%) DO (
IF !Line!==!RandomLine! (
REM Found the line. Copy the file to the destination.
XCOPY /V /Y "%%F" %Destination%
) ELSE (
REM Not the random file, build the new list without this file included.
ECHO %%F>> %FileList2%
)
SET /A Line=!Line! + 1
)
SET /A TotalFiles=!TotalFiles! - 1
REM Update the master file list with the new list without the last file.
DEL /F /Q %FileList1%
RENAME %FileList2% %FileList1Name%
)
IF EXIST %FileList1% DEL /F /Q %FileList1%
IF EXIST %FileList2% DEL /F /Q %FileList2%
ENDLOCAL
The destination should be set in the .bat code like the code above. Can anybody please help me with this? Thanks in advance!
Copying a directory tree structure (folders only) is trivial with XCOPY.
Selecting a random file from a given folder is not too difficult. First you need the count of files, using DIR /B to list them and FIND /C to count them. Then use the modulo operator to select a random number in the range. Finally use DIR /B to list them again, FINDSTR /N to number them, and another FINDSTR to select the Nth file.
Perhaps the trickiest bit is dealing with relative paths. FOR /R can walk a directory tree, but it provides a full absolute path, which is great for the source, but doesn't do any good when trying to specify the destination.
There are a few things you could do. You can get the string length of the root source path, and then use substring operations to derive the relative path. See How do you get the string length in a batch file? for methods to compute string length.
Another option is to use FORFILES to walk the source tree and get relative paths directly, but it is extremely slow.
But perhaps the simplest solution is to map unused drive letters to the root of your source and destination folders. This enables you to use the absolute paths directly (after removing the drive letter). This is the option I chose. The only negative aspect of this solution is you must know two unused drive letters for your system, so the script cannot be simply copied from one system to another. I suppose you could programatically
discover unused drive letters, but I didn't bother.
Note: It is critical that the source tree does not contain the destination
#echo off
setlocal
:: Define source and destination
set "source=c:\mySource"
set "destination=c:\test2\myDestination"
:: Replicate empty directory structure
xcopy /s /t /e /i "%source%" "%destination%"
:: Map unused drive letters to source and destination. Change letters as needed
subst y: "%source%"
subst z: "%destination%"
:: Walk the source tree, calling :processFolder for each directory.
for /r y:\ %%D in (.) do call :processFolder "%%~fD"
:: Cleanup and exit
subst y: /d
subst z: /d
exit /b
:processFolder
:: Count the files
for /f %%N in ('dir /a-d /b %1 2^>nul^|find /c /v ""') do set "cnt=%%N"
:: Nothing to do if folder is empty
if %cnt% equ 0 exit /b
:: Select a random number within the range
set /a N=%random% %% cnt + 1
:: copy the Nth file
for /f "delims=: tokens=2" %%F in (
'dir /a-d /b %1^|findstr /n .^|findstr "^%N%:"'
) do copy "%%D\%%F" "z:%%~pnxD" >nul
exit /b
EDIT
I fixed an obscure bug in the above code. The original COPY line read as follows:
copy "%%~1\%%F" "z:%%~pnx1" >nul
That version fails if any of the folders within the source tree contain %D or %F in their name. This type of problem always exists within a FOR loop if you expand a variable with %var% or expand a :subroutine parameter with %1.
The problem is easily fixed by using %%D instead of %1. It is counter-intuitive, but FOR variables are global in scope as long as any FOR loop is currently active. The %%D is inaccessible throughout most of the :processFolder routine, but it is available within the FOR loops.
The "natural" way to process a directory tree is via a recursive subroutine; this method minimize the problems inherent to this process. As I said at this post: "You may write a recursive algorithm in Batch that gives you exact control of what you do in every nested subdirectory". I taken the code at this answer, that duplicate a tree, and slightly modified it in order to solve this problem.
#echo off
setlocal
set "Destination=H:\Temp"
set "FileFilter=*.ape"
rem Enter to source folder and process it
cd /D "%~dp1"
call :processFolder
goto :EOF
:processFolder
setlocal EnableDelayedExpansion
rem For each folder in this level
for /D %%a in (*) do (
rem Enter into it, process it and go back to original
cd "%%a"
set "Destination=%Destination%\%%a"
if not exist "!Destination!" md "!Destination!"
rem Get the files in this folder and copy a random one
set "n=0"
for %%b in (%FileFilter%) do (
set /A n+=1
set "file[!n!]=%%b"
)
if !n! gtr 0 (
set /A "rnd=!random! %% n + 1"
for %%i in (!rnd!) do copy "!file[%%i]!" "!Destination!"
)
call :processFolder
cd ..
)
exit /B
Here is anther approach using xcopy /L to walk through all files in the source directory, which does not actually copy anything due to /L but returns paths relative to the source directory. For explanation of the code see all the remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define source and destination directories here:
set "SOURCE=%dp~1"
set "DESTIN=H:\Temp"
rem Change to source directory:
cd /D "%SOURCE%"
rem Reset index number:
set /A "INDEX=0"
rem Walk through output of `xcopy /L`, which returns
rem all files in source directory as relative paths;
rem `find` filters out the summary line; `echo` appends one more line
rem with invalid path, just to process the last item as well:
for /F "delims=" %%F in ('
2^> nul xcopy /L /S /I /Y "." "%TEMP%" ^
^| find ".\" ^
^& echo^(C:\^^^|\^^^|
') do (
rem Store path to parent directory of current item:
set "CURRPATH=%%~dpF"
setlocal EnableDelayedExpansion
if !INDEX! EQU 0 (
rem First item, so build empty directory tree:
xcopy /T /E /Y "." "%DESTIN%"
endlocal
rem Set index and first array element, holding
rem all files present in the current directory:
set /A "INDEX=1"
set "ITEMS_1=%%F"
) else if "!CURRPATH!"=="!PREVPATH!" (
rem Previous parent directory equals current one,
rem so increment index and store current file:
set /A "INDEX+=1"
for %%I in (!INDEX!) do (
endlocal
set /A "INDEX=%%I"
set "ITEMS_%%I=%%F"
)
) else (
rem Current parent directory is not the previous one,
rem so generate random number from 1 to recent index
rem to select a file in the previous parent directory,
rem perform copying task, then reset index and store
rem the parent directory of the current (next) item:
set /A "INDEX=!RANDOM!%%!INDEX!+1"
for %%I in (!INDEX!) do (
xcopy /Y "!ITEMS_%%I!" "%DESTIN%\!ITEMS_%%I!"
endlocal
set /A "INDEX=1"
set "ITEMS_1=%%F"
)
)
rem Store path to parent directory of previous item:
set "PREVPATH=%%~dpF"
)
endlocal
exit /B
For this approach the destination directory can also be located within the source directory tree.
i have a lot of folders with a random number of files within.
And the most of them just have 1 file inside it, these files needs to move to the parent folder. but i don't know how i should do it with a batch file.
I use Windows 7 Ultimate
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /d /r "%sourcedir%" %%a IN (*) DO (
FOR /f %%c IN ('dir /b/a-d "%%a\*.*" 2^>nul ^|find /c /v ""') DO IF %%c==1 ECHO(MOVE "%%a\*.*" "%%a\..\"
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
The for /d /r finds all of the subdirectory names starting at sourcedir and assigns thenm to %%a in turn.
Each directory is then examined for filenames only; the 2>nul suppresses error messages for empty directories, and the output of the dir command is fed to find which counts (/c) the number of lines which don't match "" (ie. counts the lines of directory = # files returned). This is applied to %%c
If %%c is 1, then move all (one) files from the directory to its parent.
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)
This solution don't use any external .exe command, so it should run faster. Also, it is clear enough to be understood with no problems, I think...
#echo off
setlocal EnableDelayedExpansion
for /D %%d in (*) do (
set "firstFile="
set "moreThanOneFile="
for %%f in ("%%d\*.*") do (
if not defined firstFile (
set "firstFile=%%f"
) else (
set "moreThanOneFile=true"
)
)
if not defined moreThanOneFile move "!firstFile!" "%%d"
)
You have a number of folders that all may or may not contain files within them, and you want to move all files to some other directory?
PowerShell is the new and improved super charged version of using a batch file, and this is how you'll do it.
Run PowerShell
paste in the following
dir -Recurse C:\FolderContainingManySubfolder | ? PSIsContainer | dir -Recurse | move-item -Destination T:\somePath -WhatIf
Replace C:\FolderContainingManySubfolder with the folder that has all of those other folders with one or two items each, and replace T:\somePath with the place you want the single files to go.
Run it, and you'll see a lot of 'What If' output, which shows you what would happen if you were to run the command. If you're happy with what you see, then remove the -WhatIf parameter from the end.
I have a folder containing a bunch of subfolders and files, but the structure is a bit inefficient. For example:
Root Folder
----EmptyFolder1
--------Folder1
------------SubFolder1
------------File1
------------File2
----EmptyFolder2
--------Folder2
------------SubFolder2
------------File3
------------File4
How can I move all of the Folders/SubFolders/Files up in the tree and eliminate all of the EmptyFolders so that it looks more like this:
Root Folder
----Folder1
--------SubFolder1
--------File1
--------File2
----Folder2
--------SubFolder2
--------File3
--------File4
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t h r e e"
FOR /f "delims=" %%a IN (
'dir/s/b/a-d "%sourcedir%"'
) DO (
CALL :movefile %%a
)
:loop
SET "zapped="
FOR /d /r "%sourcedir%" %%a IN (.) DO (
RD "%%a" >NUL 2>NUL
IF NOT EXIST "%%a" SET zapped=Y
)
IF DEFINED zapped GOTO loop
DIR /s/b/ad "%sourcedir%
GOTO :EOF
:movefile
SET "oldfn=%*"
SET "newfn=!oldfn:%sourcedir%\=!"
SET "newfn=%sourcedir%\%newfn:*\=%"
FOR %%r IN ("%newfn%") DO (
ECHO MD "%%~dpr"
ECHO MOVE "%oldfn%" "%newfn%"
)
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
caution test on a representative subtree first!
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
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 section after the :loop label deletes any empty directories in the subtree. Of necessity, the commands are executed not merely displayed.
Be very, very careful about file/directorynames that contain symbols with special meaning to cmd.
How to remove spaces (not replace with underscores) from several thousand files in bulk in Windows? Can I do this from the DOS command?
Currently:
file one.mp3
file two.mp3
All files need to become:
fileone.mp3
filetwo.mp3
Here is a script that can efficiently bulk rename files, stripping all spaces from the name.
:renameNoSpace [/R] [FolderPath]
#echo off
setlocal disableDelayedExpansion
if /i "%~1"=="/R" (
set "forOption=%~1 %2"
set "inPath="
) else (
set "forOption="
if "%~1" neq "" (set "inPath=%~1\") else set "inPath="
)
for %forOption% %%F in ("%inPath%* *") do (
if /i "%~f0" neq "%%~fF" (
set "folder=%%~dpF"
set "file=%%~nxF"
setlocal enableDelayedExpansion
echo ren "!folder!!file!" "!file: =!"
ren "!folder!!file!" "!file: =!"
endlocal
)
)
Assume the script is called renameNoSpace.bat
renameNoSpace : (no arguments) Renames files in the current directory
renameNoSpace /R : Renames files in the folder tree rooted at the current directory
renameNoSpace myFolder : Renames files in the "myFolder" directory found in the current directory.
renameNoSpace "c:\my folder\" : Renames files in the specified path. Quotes are used because path contains a space.
renameNoSpace /R c:\ : Renames all files on the C: drive.
In Windows:
Open a Command Prompt.
Go to the folder with the cd command (eg.: cd "paht of your folder").
Open a powershell by typing: powershell
Then input this: get-childitem *.mp3 | foreach {rename-item $_ $_.name.replace(" ","")}
Create a powershell file - *.ps1 extension
Write this code:
dir |
Where-Object { $_.name.Contains(" ") } |
Rename-Item -NewName { $_.name -replace " ","" }
save, then right click -> run with powershell
Open Powershell in windows and then type the following command after navigating to the folder where you want to rename the files
get-childitem *.mp3 | foreach { rename-item $_ $_.Name.Replace(" ", "") }
Let's Analyze it:
get-childitem *.mp3
This lists all files whose names end with .mp3. They are then piped to the next command with the | operator.
foreach { rename-item $_ $_.Name.Replace(" ", "") }
This replaces all instances of " " (white space in this case || this is the instance which is to be replaced) with nothing, denoted by "", effectively wiping the word from all the files in the directory.
You could also modify get-childitem *.mp3 to get-childitem – that would rename all the files in the directory, not just files whose names end with .mp3.
** Note **
If you do not like the above method there is a awesome software named Bulk Rename Utility. I used this software personally and you can get many tuts on youtube how to use it.
You can write a simple script that does this for one file/directory, e.g.:
#echo off
setlocal enableextensions enabledelayedexpansion
set "ARG=%~1"
ren "%ARG%" "%ARG: =%"
...and then if you'd like, run it over all the files and/or directories you care about. For instance, if you create the above script as myrenamingscript.cmd, you can run it over all non-dir files in the current dir by running:
for %f in (*) do #myrenamingscript.cmd "%~f"
#echo off
setlocal enableextensions enabledelayedexpansion
for %%f in (*.*) do (
set ARG=%%~nxf
rename "%%f" !ARG: =!
)
Since programmatically renaming files is risky (potentially destructive if you get it wrong), I would use a tool with a dry run mode built specifically for bulk renaming, e.g. renamer.
This command strips out the whitespace from all files in the current directory:
$ renamer --find "/\s/g" --dry-run *
Dry run
✔︎ file one.mp3 → fileone.mp3
✔︎ file two.mp3 → filetwo.mp3
Rename complete: 2 of 2 files renamed.
Plenty more renamer usage examples here.
The problem i have faced is that there is a possibility that there is already a file with the name you try to give to the new file (eg if there are 2 files in the folder named "file one.txt" and "file_one.txt" when you try to replace the spaces with underscores, one file will replace the other). So I made this script that checks if the new name already exists and if so places a number at the end of the file name (adds 1 to the number until there is no other file with that name). Instructions about what to change are at the top (commended out lines). Do not store the batch file in the same folder you have the files to be renamed if you use *.* option. I hope this helps.
#echo off
REM Instructions
REM This script repaces spaces from file names with underscores.
REM If you want to just remove the spaces uncomment lines 30 and 52 and comment out the lines 29 and 51.
REM set the following parameters.
REM pb is the folder containing the files we want to rename (fullpath)
REM tm is a temporary folder that will be created and deleted. Just put a folder that does not exist and is not used by anything else (fullpath).
REM all is the file type you want to raname. E.g. *.* for every file, *.txt for TXTs, *.pdf for PDFs etc
REM you don't have to change anything else
set pb=<folder containing the files to rename>
set tm=<a temp folder that does not exist>
set all=*.*
set pa=%pb%%all%
setlocal EnableDelayedExpansion
cd /d %pa%
set /a count=1
if not exist %tm% mkdir %tm%
for /f %%F in (%pa%) do (
set name=%%~nF
set name2=!name: =_!
REM set name2=!name: =!
set name3=!name2!%%~xF
if !name2! == %%~nF (
move /y %%~dpF\!name3! %tm%\ >nul
) else (
if not exist %%~dpF\!name3! (
if not exist %tm%\!name3! (
ren "%%F" "!name3!"
move /y %%~dpF\!name3! %tm%\ >nul
)
)
)
)
:rename
for /f %%F in (%pa%) do (
set name=%%~nF
set name2=!name: =_!
REM set name2=!name: =!
set name4=!name2!%count%
set name3=!name4!%%~xF
if !name2! == %%~nF (
move /y %%~dpF\!name3! %tm%\ >nul
) else (
if not exist %%~dpF\!name3! (
if not exist %tm%\!name3! (
ren "%%F" "!name3!"
move /y %%~dpF\!name3! %tm%\ >nul
)
)
)
)
set /a count = %count% + 1
set /a loop = 0
for %%F in (%pa%) do (set /a loop = 1)
if %loop% equ 1 goto rename
move /y %tm%\%all% %pb% >nul
rmdir /s /q %tm%
Just copy paste this to batch-file and save as c:\myfolder\r.bat
setlocal EnableDelayedExpansion
#echo off &cls
set p=%*
set p=!p: =!
set p=!p:^(=!
set p=!p:^)=!
set p=!p:^[=!
set p=!p:^]=!
set p=!p:^$=!
set p=!p:^&=!
set p=!p:^#=!
set p=!p:^;=!
set p=!p:^:=!
set p=!p:^,=!
set p=!p:^#=!
set p=!p:^_=!
set p=!p:^-=!
set e=%p:~-4%
ren "%*" %time:~6,2%%time:~9,2%%e%
exit
Add a registry ket at
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\#myRENAME]
#=""
[HKEY_CLASSES_ROOT\*\shell\#myRENAME\command]
#="c:\myfolder\r.bat \"%1\""
done, now select files & just right-click select myRENAME
all the files will be renamed instantly.