I recently started creating some small batch files, as I think that Batch in itself is interesting, but also because it helps automate some things that I do on a regular basis.
The script that I'm working on is a program that deletes all directories in a folder (As of now put in as the %UserProfile%), and allows all entires in an Exception file to be ignored. I don't want Exact Paths, I wanted it so that if the exception was present anywhere in the folders path, then it needs to be excluded.
Here's what I have (Keep in mind I'm not very good):
#ECHO OFF
CD %UserProfile%
SET /A Violation=0
SET "ExceptionFile=%*"
SETLOCAL ENABLEDELAYEDEXPANSION
SHIFT
ECHO Starting...
ECHO Scan started at %TIME% > "%UserProfile%\Log.txt"
FOR /f "usebackq delims=" %%D in (`"dir /ad/b/s | sort /R"`) DO CALL :DIREXPANDER "%%D"
ECHO Scan Finished at %TIME% >> "%UserProfile%\Log.txt"
GOTO:EOF
:DIREXPANDER
SET /A Violation=0
SET Directory=%1
ECHO %Directory%
FOR /F %%X IN (%ExceptionFile%) DO (
SET "Exception=%%X"
ECHO %Directory% Compared to %Directory:!Exception!=% >> "%UserProfile%\Test.txt"
If NOT %Directory%==%Directory:!Exception!=% (
ECHO EXCEPTION: %Directory% Violates !Exception! >> "%UserProfile%\Log.txt"
REM SET /A Violation=1
)
)
IF %Violation%==0 (
RMDIR %Directory% 2>NUL && ECHO DELETION: %Directory% >> "%UserProfile%\Log.txt"
)
GOTO:EOF
If I replace !Exception! With an actual String, it works just fine--but when using a variable it doesn't seem to work.
Thanks for any suggestions or issues that you notice!
Pretty sure you can write this without using the CALL to the Label.
#ECHO OFF
CD %UserProfile%
SET "ExceptionFile=%*"
SETLOCAL ENABLEDELAYEDEXPANSION
SHIFT
ECHO Starting...
ECHO Scan started at %TIME% > "%UserProfile%\Log.txt"
FOR /f "usebackq delims=" %%D in (`"dir /ad/b/s | sort /R"`) DO (
SET "Violation=0"
SET "Directory=%%D"
ECHO %%D
FOR /F %%X IN (%ExceptionFile%) DO (
ECHO %%D Compared to !Directory:%%X=! >> "%UserProfile%\Test.txt"
If NOT "%%D"=="!Directory:%%X=!" (
ECHO EXCEPTION: %%D Violates %%X >> "%UserProfile%\Log.txt"
SET "Violation=1"
)
)
IF "!Violation!"=="0" (
RMDIR "%%D" 2>NUL && ECHO DELETION: %%D >> "%UserProfile%\Log.txt"
)
)
ECHO Scan Finished at %TIME% >> "%UserProfile%\Log.txt"
This nested code was 5 seconds faster on my computer.
Related
This question already has an answer here:
Variables are not behaving as expected
(1 answer)
Closed 24 days ago.
I've been racking my head over my script on why the section for finding out the directory size in bytes does not work but after a long time of debugging i've encountered the following scenario:
if I run the for loop inside an IF statement it doesn't work
if I run the for loop outside the IF statement it does work
if I run the for loop outside the IF statement and then run it inside afterwards, it does work
Is the for loop written incorrectly or am I misunderstanding or missing something?
The following does NOT work
#ECHO OFF
set "test=1"
if %test% GTR 0 (
echo Scenario A
pushd "%~dp0"
set dir="C:\temp"
for /f "tokens=3" %%i in ('robocopy /l /e /bytes %dir% %dir% ^| findstr Bytes') do set size=%%i
echo Size = %size%
pause
) else (
echo Scenario B
pause
)
But this DOES work for both runs
#ECHO OFF
set "test=1"
echo Method a
pushd "%~dp0"
set dir="C:\temp""
for /f "tokens=3" %%i in ('robocopy /l /e /bytes %dir% %dir% ^| findstr Bytes') do set size=%%i
echo Size = %size%
echo Now Method A in IF statement
if %test% GTR 0 (
echo Scenario A
pushd "%~dp0"
set dir="C:\temp"
for /f "tokens=3" %%i in ('robocopy /l /e /bytes %dir% %dir% ^| findstr Bytes') do set size=%%i
echo Size = %size%
pause
) else (
echo Scenario B
pause
)
The reason is that variables in if/else statement are expanded before executing it. You are setting dir variable inside it, but all %dir% occurrences are expanded at the same time, before running if/else.
Basically the following command is actually executed. As you can see all variables were expanded:
if 1 GTR 0 (
echo Scenario A
pushd "C:\Workdir\"
set dir="C:\temp"
for /f "tokens=3" %i in ('robocopy /l /e /bytes | findstr Bytes') do set size=%i
echo Size =
pause
) else (
echo Scenario B
pause
)
As an alternative you can use if/goto commands to organize the flow. In this case every command will be expanded individually. It will look like this:
if %test% NEQ 1 goto ScenarioB
rem Scenario A
goto Done
:ScenarioB
rem Scenario B
:Done
Also double quotes are not needed in set test=1.
To troubleshoot such issues you can turn echo on, then you will see the commands being executed.
I'm trying to delete every file bigger than a certain size in a directory but i'm not able to delete file with space in the name. The below script is what I have written to achieve the same.
#echo off
setlocal enabledelayedexpansion
set "MaxSize=3"
for /r %%I in (*) do (
echo %%I %%~zI Bytes
set /a kb=%%~zI/1024 + 1
echo !kb!Ko
if !kb! GTR %MaxSize% (
echo TIME : [%date%, %time%] ^| The size of the file %%I is !kb! Ko
the file is to big so the file was deleted >> Log_Remove.log
del /F %%I
echo file too big the file was deleted
) else (
echo file size is okay
)
)
Any idea about how i can delete the file with a space in the name ?
I've posted this as an addition to my comment, to show you an easier way of structuring your script.
#Echo off
Set "MaxKB=3"
Set /A MaxB=MaxKB*1024
For /R %%I In (*)Do If %%~zI Gtr %MaxB% (
Echo [%DATE%, %TIME%]: %%I was too large and therefore deleted>>"Log_Remove.log"
Del /A /F "%%I"
Echo file %%I was larger than %MaxKB% KB
)Else Echo file %%I was within %MaxKB% KB
I have a script that successfully scans a specified directory for a list of files, copies the files to a destination folder and generates a log of all files not found in the source. I had to alter this script to include net use in order to map a network drive. Since doing so the script no longer generates the error log as it did before.
I'm very new to this and can't find any information on why this may be happening. Can somebody please help?
#echo off
pause
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
net use U: \\abc\def
SET "src="U:\Source Folder""
SET "dst=C:\Destination Folder"
SET "file_list=C:\files.txt"
SET "out=.\errors.log"
echo > %out%
FOR /F "usebackq eol=| delims=" %%f IN ("%file_list%") DO (
rem just searching to find out existense of file
WHERE /Q /R %src% "%%f"
IF "!ERRORLEVEL!" == "0" (
FOR /F "usebackq eol=| delims=" %%s IN (`WHERE /R %src% "%%f"`) DO (
echo "%%s => %dst%\%%f" >> %out%
#copy /y "%%s" "%dst%\%%f"
)
) ELSE (
echo %%f >> %out%
)
)
Replace echo %%f >> %out% with set /p out=%%f
[Edit]
Sorry I misunderstood your question.
Is it because you have two quotation marks in all of the drive path set?
As in replace
SET "src="U:\Source Folder""
With
SET src="U:\Source Folder"
I am newbie with windows batch command so please spare me for any irrelevant help.
Basically i want to merge certain files using type command of windows but since those files are coming from various sources i need to search in file name for source filter and only merge those files. i have tried writing below code but it's not doing the job for me.
#echo off
set filter=%1
set final_file=%2
echo %filter%
echo %final_file%
for %f in (*.dlt) do(
echo %f
if find %filter "%f (
do type "%f" >> %final_file
)
)
Here is an example that i made to merge all *.bat files in one file; so you can easily modify it to your needs :
Just you need to modify the variable Set "Filter_Ext=dlt" and the Set "MasterFolder=%userprofile%\desktop" to yours
#echo off
Mode 75,3 & Color 9E
Title Merge all *.bat in one file
Set "MasterFolder=%userprofile%\desktop"
Set "OutPut=Output_Merged_Files.txt"
Set "Filter_Ext=bat"
If exist "%OutPut%" Del "%OutPut%"
echo(
echo Please Wait a while we generate the output file ...
#For /f "delims=" %%a in ('Dir /s /b /A-D "%MasterFolder%\*.%Filter_Ext%"') Do (
cls
echo(
echo Please Wait a While ... Merging "%%~nxa" ...
(
echo ====================================================
echo Contents of "%%a"
echo ====================================================
Type "%%a"
echo(
)>> "%OutPut%"
)
Start "" "%OutPut%"
Edit Merge all .dlt in one file
#echo off
Mode 75,3 & Color 9E
Title Merge all *.dlt in one file
Set "MasterFolder=%~1"
Set "OutPut=Output_Merged_Files.txt"
Set "Filter_Ext=dlt"
Set "KeyWord=Engine"
If exist "%OutPut%" Del "%OutPut%"
echo(
echo Please Wait a while we generate the output file ...
#For /f "delims=" %%a in ('Dir /s /b /A-D "%MasterFolder%\*.%Filter_Ext%" ^|find /I "%KeyWord%"') Do (
cls
echo(
echo Please Wait a While ... Merging "%%~nxa" ...
(
echo ====================================================
echo Contents of "%%a"
echo ====================================================
Type "%%a"
echo(
)>> "%OutPut%"
)
Start "" "%OutPut%"
Right now I've got a batch script that is used to go through a bunch of subfolders, and zip up the logfiles therein, the folder structure is basically like this:
+---fakeG
| +---ExclusionFolder
| | +---LimitlessFolders
| | +---MoreFolders
| | \---SoManyFolders
| +---logs1
| +---logs2
| +---logs3
| +---logs4
| +---logs5
| \---logs6
Each of these subfolders must be traversed, and their subfolders traversed as well. This script has to avoid one specific folder, and it does avoid it, but it uses %| find /v. Although in this example, ExclusionFolder is at the top of the list, in the real folder structure it is not.
Here is how I do it now:
FOR /F "delims=" %%A IN (
'DIR "%LogsFilespec%" /B /S ^| find /v "ExclusionFolder"'
) DO CALL :DoZip "%%~dpnA" "%%~fA" "%zipCommand%" "%zipParams%" %ProcessLog%
Inside ExclusionFolder, there are more subfolders, with potentially many more subfolders, so it would slow down the scripts execution having to go into each of them.
SO: Is there a faster way to exclude a folder from a dir call without |find ?
Or do I have to come up with a totally different way to do this?
Edit Sorry, %LogsFileSpecâ„… refers to the target file. My original edit was right, and my second edit was wrong.
SET LogsLocation="G:\fakeG\Data\Logs"
SET LogsName="trace*%targetDate%.log"
SET LogsFilespec="%LogsLocation%\%LogsName%"
Sorry for not giving more of the script, I figured the question didn't need much.
Edit2 The process :DoZip works like this:
:DoZip
:: Parameter 1 = Filename without .EXT for Archive Name
:: Parameter 2 = Target file specifics
:: Parameter 3 = Zip Command (Winzip/7zip64/7zip32)
:: Parameter 4 = Zip Parameters (a -tzip, -a)
:: Parameter 5 = ProcessLog
setlocal
SET archiveName=%~1
SET SourceFileSpec=%~2
SET zipCommand=%~3
SET zipParms=%~4
SET RunLog=%~5
ECHO %TIME% Archiving %SourceFileSpec%...
ECHO %TIME% Archiving %SourceFileSpec%... >> %RunLog%
ECHO "%zipCommand%" %zipParms% "%archiveName%.zip" "%SourceFileSpec%"
ECHO "%zipCommand%" %zipParms% "%archiveName%.zip" "%SourceFileSpec%" >> %RunLog%
"%zipCommand%" %zipParms% "%archiveName%.zip" "%SourceFileSpec%" >> %RunLog%
:: Check errorlevel of executed command
:: If errorlevel != 0, set EC with the errorlevel and echo that there was an error in archival
IF NOT %ERRORLEVEL%==0 (
ECHO ***ERROR archiving %SourceFileSpec% >> %RunLog%
SET EC=%ERRORLEVEL%
ECHO ***ERRORLEVEL RETURNED: %EC% >> %RunLog%
) ELSE (
:: Otherwise, delete the file
ECHO. >> %RunLog%
ECHO. >> %RunLog%
ECHO %TIME% Deleting %SourceFileSpec%...
ECHO %TIME% Deleting %SourceFileSpec%... >> %RunLog%
::Quietly delete the file
DEL /Q %SourceFileSpec%
:: Set ErrorLevel to capture the Delete command result.
SET EC=%ERRORLEVEL%
)
GOTO :EOF
Edit 3 Here is zipCommand
SET PathWinZip=C:\Program Files\WinZip\wzzip.exe
SET Path7Zip_64bit=C:\Program Files\7-Zip\7z.exe
SET Path7Zip_32bit=C:\Program Files (x86)\7-Zip\7z.exe
:: Check for WinZip
IF EXIST "%PathWinZip%" SET zipCommand=%PathWinZip% & SET zipParms=-a
:: Check for 32-bit version of 7-Zip. If found, configure
:: its command line parameter to produce a .zip file
IF EXIST "%Path7Zip_32bit%" SET zipCommand=%Path7Zip_32bit% & SET zipParms=a -tzip
:: Check for 64-bit version of 7-Zip. If found, configure
:: its command line parameter to produce a .zip file
IF EXIST "%Path7Zip_64bit%" SET zipCommand=%Path7Zip_64bit% & SET zipParms=a -tzip
I believe that the following should exclude the subtree in question:
FOR /F "delims=" %%X IN (
'DIR /B /ad ') DO IF /i "%%X" neq "ExclusionFolder" FOR /F "delims=" %%A IN (
'DIR /B /S "%%X\%LogsFilespec%"'
) DO CALL :DoZip "%%~dpnA" "%%~fA" "%zipCommand%" "%zipParams%" %ProcessLog%
That is, perform a directory-name scan of the target; if the directory found is not the exclude-name, then do the remainder for that subdirectory.
This approach could be a bit faster:
pushd "%LogsFilespec%"
rem get subfolders of first level only
for /f "tokens=*" %%p in (
'DIR "%LogsFilespec%" /B /AD ^| find /v "ExclusionFolder"'
) do (
rem treat each (here will not be excluded one)
FOR /F "delims=" %%A IN (
'DIR "%%~p" /B /S'
) DO CALL :DoZip "%%~dpnA" "%%~fA" "%zipCommand%" "%zipParams%" %ProcessLog%
)
popd
goto :eof
The solution below don't use a single FIND command nor a FOR /F ... one (that requires the execution of a copy of cmd.exe), so I think it should run faster:
#echo off
setlocal
rem Enter to the base folder
cd "%LogsFilespec%"
rem Start the recursive process
call :processThisDir
goto :EOF
:processThisDir
rem Process the files in this folder
for %%A in (*.*) do CALL :DoZip "%%~dpnA" "%%~fA" "%zipCommand%" "%zipParams%" %ProcessLog%
rem Process nested subfolders, but omit "ExclusionFolder"
for /D %%a in (*) do (
if "%%a" neq "ExclusionFolder" (
cd "%%a"
call :processThisDir
cd ..
)
)
exit /B