Variable not setting in nested for loop - Windows batch - for-loop

Below is a simplified snippet of my code:
#echo off
setlocal enabledelayedexpansion
for /f %%f in ('%pomFiles%') do (
findstr "var" %%f > nul
if errorlevel 0 if not errorlevel 1 (
cd "%%~dpf"
for /f "usebackq" %%i in ('%%~dpftree.out') do ( set size=%%~zi && echo !size!)
if !size! gtr 0 (
//do stuff
)
)
)
The !size! variable does not seem to be being set. If I do echo !size!, it prints !size!. How can I make sure !size! evaluates?

It's because you are using for /f, which is to read the contents of a file, hence the problem is you are trying to get the size of the variable %%i, which is set as the first line in the file.
You just need a regular for loop
for %%i in ("%%~dpftree.out") do ( set size=%%~zi && echo !size!)
if !size! gtr 0 (
//do stuff
)

Related

log file of script batch

#echo off
call :checkFTP1 %* > all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :checkFTP2 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :checkFTP3 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands1 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands2 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands3 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
call :doCommands4 %* >> all_log_all_log_%date:~10,4%%date:~4,2%%date:~7,2%.log 2>&1
exit /b
:checkFTP1
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder1"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit1
) ELSE (
goto :checkFTP2
)
Endlocal
:checkFTP2
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder2"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit2
) ELSE (
goto :checkFTP3
)
Endlocal
:checkFTP3
#echo off
Setlocal
:: Is folder empty
set _TMP=
for /f "delims=" %%a in ('dir /b "C:\test\folder3"') do set _TMP="%%a"
IF {%_TMP%}=={} (
goto :Exit3
) ELSE (
goto :doCommands1
)
Endlocal
:doCommands1
call script1.bat
if %errorlevel% EQU 0 (goto :doCommands2 ) Else ( ECHO error on script 1 ,2,3,4)
exit
:doCommands2
call script2.bat
if %errorlevel% EQU 0 (goto :doCommands3 ) Else ( ECHO Script 1 Completed Successfully , ERRORS on 2,3,4)
exit
:doCommands3
call script3.bat
if %errorlevel% EQU 0 (goto :doCommands4) Else ( ECHO Script 2 Completed Successfully , ERRORS on 3,4)
exit
:doCommands4
call script4.bat
if %errorlevel% EQU 0 (goto :completed1) Else ( ECHO Script 3 Completed Successfully , ERRORS on 4)
exit
:Exit1
Echo Today Date %DATE% at %Time%
Echo ###################FTP-1 FILES MISSING #########################
Exit
:Exit2
Echo Today Date %DATE% at %Time%
Echo ###################FTP-2 FILES MISSING (#########################
Exit
:Exit3
Echo Today Date %DATE% at %Time%
Echo ###################FTP-3 FILES MISSING #########################
Exit
:completed1
Echo Today Date %DATE% at %Time%
Echo ###################all scripts Completed Successfully#########################
Exit
I have above batch file which calls multiple bat files. I have tested the script and it worked fine.
My only issue is that the log file generated contains all information, and it's a large file.
Is it possible to just log comments and echo, and exclude what executed in screen?
For example I don't want 1 file moved to be showing in log file.
I've taken your code and re-written it to use generic functions which deduplicates the code into an a more easily managed form and allows you to add or remove any steps to FTP and Script sections as needed by editing the list of variables.
I also only return output that does not include the "file(s) moved".
Alternatively if you really only want the status lines to print you could change this to just have those print to the log and not have all info print to the log (this is being done because you are putting the redirection to the log on the calls to other steps.
Also the way this was written before it was actually going through all the goto commands and not needing the calls to each at the top.
Here is the refactored code, I haven't tested it but it should be fine I have to step out for a few hours and you can ask any questions and I'll be happy to answer when I have time.
#( Setlocal EnableDelayedExpansion
echo off
SET "_FolderList="C:\test\folder1" "C:\test\folder1" "C:\test\folder1" "
SET "_ScriptList="c:\test\script1.bat" "E:\script2.bat" "C:\Path\To\script3.bat" "C:\Path\To\script4.bat" "
SET "_Scripts_Failed=1,2,3,4"
SET "_Scripts_Completed="
SET "_Continue=0"
CALL :GetDateTime
SET "MasterLog=all_log_all_log_!IsoDate!_!IsoTime!.log"
)
CALL :Main
( Endlocal
EXIT /B
)
:Main
SET /A "_Counter=0"
FOR %%_ IN (%_FolderList%) DO (
IF DEFINED _Continue (
SET /A "_Counter+=1"
CALL :CheckFTP_List %%~_
)
)>> "%MasterLog%"
SET /A "_Counter=0"
FOR %%_ IN (%_FolderList%) DO (
IF DEFINED _Continue (
SET /A "_Counter+=1"
CALL :DoCommands_List %%~_
)
)>> "%MasterLog%"
IF DEFINED _Continue (
Echo Today Date %DATE% at %Time%
Echo ###################all scripts Completed Successfully#########################
)
GOTO :EOF
:CheckFTP_List
REM Is folder empty
for /f "delims=" %%a in ('dir /b /A-D "%*') do (
set "_Continue=%%a" )
IF NOT Defined _Continue (
Echo.Today Date on %DATE% at %Time%
Echo.###################FTP-%_Counter% FILES MISSING #########################
)
GOTO :EOF
:DoCommands_List
call "*%" | FIND /I /V "file(s) moved" &REM only outputs lines that don't contain files moved.
if %errorlevel% NEQ 0 (
SET "_Continue="
SET "_Scripts_Completed=%_Scripts_Completed:,1=1%"
SET "_Scripts_Failed=!_Scripts_Failed:%_Scripts_Completed%=!"
Echo Today Date %DATE% at %Time%
ECHO. Error encountered on Script %Counter%! -- Completed Scripts: !_Scripts_Completed! -- Failed Scripts: !_Scripts_Failed!
) ELSE (
SET "_Scripts_Completed=%_Scripts_Completed:,1=1%,%Counter%"
)
GOTO :EOF
:GetDateTime
FOR /F "Tokens=1-7 delims=MTWFSmtwfsouehrandit:-\/. " %%A IN ("%DATE% %TIME: =0%") DO (
FOR /F "Tokens=2-4 Delims=(-)" %%a IN ('ECHO.^| DATE') DO (
SET "%%~a=%%~A"
SET "%%~b=%%~B"
SET "%%~c=%%~C"
SET "HH=%%~D"
SET "Mn=%%~E"
SET "SS=%%~F"
SET "Ms=%%~G"
)
)
SET "IsoTime=%HH%.%Mn%.%SS%.%Ms%"
SET "IsoDate=%yy%-%mm%-%dd%"
GOTO :EOF

How to use % or ! in batch script

I write script like this:
#ECHO OFF
setlocal EnableDelayedExpansion
set "remove=ABC"
echo. %remove%
Set FILENAME="456_789_ABC00011092_789_EFGHIK_56893.mpg"
for %%a in (%FILENAME:_=" "%) do (
set TEN=%%a
echo. %AB%
set "remove_1=ABC"
echo. %remove_1%
Set _TEN=!TEN:%remove%=!
echo. %_TEN%
Set i=0
IF !_TEN! NEQ !TEN! (
set /A i+=1
set "String[!i!]=%%~a"
)
)
pause
exit
Why echo. %AB% echo. %remove_1% result is
I replace % by !. It's work fine but command Set _TEN=!TEN:!remove_1!=! not run
Edit - (from the additional question currently posted as an answer)
When I use FindStr command like this:
for %%a in (%FILENAME:_=" "%) do (
echo %%a | findstr /I /R /C:"ABC" >nul
ECHO %errorlevel%
if "%errorlevel%" equ "0" (
set /A i+=1
set "String[!i!]=%%~a"
)
)
Why errorlevel always = 0
%AB% has not been defined within your posted script, so as it has no value will not be echoed, you will just get an empty line due to the . after echo. Because remove_1 is being set within the loop, (code block), you should be using the delayed expansion syntax, Echo !remove_1!. It is the same for echo. %_TEN%, i.e. Echo !_TEN!, and would have been Echo !AB! had it previously been defined. In order to get the double expansion needed to Set your _TEN variable, you could use a pseudo Call:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Set "TEN=%%A"
Echo. !AB!
Set "remove_1=ABC"
Echo !remove_1!
Call Set "_TEN=!TEN:%%remove_1%%=!"
Echo !_TEN!
Set "i=0"
If "!_TEN!" NEq "!TEN!" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Pause
Exit /B
In your second related question, initially posted as an answer and now added as an edit to your original question; because the error level is being set within the loop, (code block), you should be using the delayed expansion syntax, !errorlevel!
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul
Echo !errorlevel!
If "!errorlevel!"=="0" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B
Or if you don't need to Echo each error level to the screen, you can use a conditional statement &&:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul && (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B

Windows batch file to list all duplicates (and the original file) in tree and sort them

I have to check a tree for duplicating files and write all of them to List.txt file.
But my script seems to skip one of the file locations in each group. (For example, if there are 4 duplicating files, only 3 of them appear in the list.)
If I'm not mistaken, it's the location of the "previousFile" of the last comparison that is missing. How do I write it to the list, too?
Also, how can I group paths in the List.txt by the filename so that it looks something like this:
File fileNameA.txt :
C:\path1\fileNameA.txt
C:\path2\fileNameA.txt
C:\path3\fileNameA.txt
File fileNameB.txt :
C:\path1\fileNameB.txt
C:\path2\fileNameB.txt
C:\path3\fileNameB.txt
C:\path4\fileNameB.txt
File fileNameC.txt :
C:\path1\fileNameC.txt
C:\path2\fileNameC.txt
...
?
That's my script so far:
#echo off
setlocal disableDelayedExpansion
set root=%1
IF EXIST List.txt del /F List.txt
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~zF:%%~fF:)|sort"'
) do (
set "currentTest=%%A"
set "currentFile=%%B:%%C"
setlocal enableDelayedExpansion
set "match="
if !currentTest! equ !previousTest! fc /b "!previousFile!" "!currentFile!" >nul && set match=1
if defined match (
echo File "!currentFile!" >> List.txt
endlocal
) else (
endlocal
set "previousTest=%%A"
set "previousFile=%%B:%%C"
)
)
You need to count matches and add echo previous filename to echo current one in case of the first match.
Note '"(for /r "%root%" %%F in (*) do #echo(%%~nxF?%%~zF?%%~fF?)|sort"' changes:
used ? (question mark) as a delimiter: reserved character by Naming Files, Paths, and Namespaces
added %%~nxF? prefix to sort output properly by file names even in my sloppy test folder structure, see sample output below.
This output shows than even cmd poisonous characters (like &, %, ! etc.) in file names are handled properly with DisableDelayedExpansion kept.
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "root=%~1"
if not defined root set "root=%CD%"
set "previousTest="
set "previousFile="
set "previousName="
set "match=0"
for /f "tokens=1-3 delims=?" %%A in (
'"(for /r "%root%" %%F in (*) do #echo(%%~nxF?%%~zF?%%~fF?x)|sort"'
) do (
set "currentName=%%A"
set "currentTest=%%B"
set "currentFile=%%C"
Call :CompareFiles
)
ENDLOCAL
goto :eof
:CompareFiles
if /I "%currentName%" equ "%previousName%" ( set /A "match+=1" ) else ( set "match=0" )
if %match% GEQ 1 (
if %match% EQU 1 echo FILE "%previousFile%" %previousTest%
echo "%currentFile%" %currentTest%
) else (
set "previousName=%currentName%"
set "previousTest=%currentTest%"
set "previousFile=%currentFile%"
)
goto :eof
Above script lists all files of duplicated names regardless of their size and content. Sample output:
FILE "d:\bat\cliPars\cliParser.bat" 1078
"d:\bat\files\cliparser.bat" 12303
"d:\bat\Unusual Names\cliparser.bat" 12405
"d:\bat\cliparser.bat" 335
FILE "d:\bat\Stack33721424\BÄaá^ cčD%OS%Ď%%OS%%(%1!)&°~%%G!^%~2.foo~bar.txt" 120
"d:\bat\Unusual Names\BÄaá^ cčD%OS%Ď%%OS%%(%1!)&°~%%G!^%~2.foo~bar.txt" 120
To list all files of duplicated names with the same size but regardless of their content:
:CompareFiles
REM if /I "%currentName%" equ "%previousName%" (
if /I "%currentTest%%currentName%" equ "%previousTest%%previousName%" (
set /A "match+=1"
REM fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
To list all files of duplicated names with the same size and binary content:
:CompareFiles
REM if /I "%currentName%" equ "%previousName%" (
if /I "%currentTest%%currentName%" equ "%previousTest%%previousName%" (
REM set /A "match+=1"
fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
Edit If the name of the file doesn't matter (only its contents), you could apply next changes in FOR loop and in :CompareFiles subroutine:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "root=%~1"
if not defined root set "root=%CD%"
set "previousTest="
set "previousFile="
set "match=0"
for /f "tokens=1-2 delims=?" %%A in (
'"(for /r "%root%" %%F in (*) do #echo(%%~zF?%%~fF?)|sort"'
) do (
set "currentTest=%%A"
set "currentFile=%%B"
rem optional: skip all files of zero length
if %%A GTR 0 Call :CompareFiles
)
ENDLOCAL
goto :eof
:CompareFiles
if /I "%currentTest%" equ "%previousTest%" (
fc /b "%previousFile%" "%currentFile%" >nul && set /A "match+=1"
) else ( set "match=0" )
if %match% GEQ 1 (
if %match% EQU 1 echo FILE "%previousFile%" %previousTest%
echo "%currentFile%" %currentTest%
) else (
set "previousTest=%currentTest%"
set "previousFile=%currentFile%"
)
goto :eof

how to compare numeric variables in batch files

why doesn't this work?
SET FIRST=""
SET COUNT=0
FOR %%F IN (dir *.png) DO (
IF %COUNT% NEQ 0 GOTO _skip
SET FIRST=%%F
:_skip
ECHO "%%F",
SET /A COUNT=COUNT+1
)
It sets FIRST to the last *.png because the IF condition fails, because COUNT - although is incremented by set /A, the IF %COUNT% doesn't ever work. Very frustrating.
Don't need to count, just do goto skip after echo line.
#echo off
for /f "delims=" %%f in ('dir /b *.png') do (
rem :: you can use "echo %%f" instead of "set first=%%f"
set first=%%f
goto _skip
)
:_skip
echo %first%
you mixing two things to scan folder.
Here is the second way:
#echo off
for %%f in (*.png) do (
set first=%%f
goto _skip
)
:_skip
echo %first%
exit /b 0
If you need absolutely to count, here is the way to skip with count. As stated in comment, you need to enable delayedExpansion
#echo off
set count=1
for %%f in (*.png) do (
set first=%%f
setlocal enabledelayedexpansion
if "!count!"=="1" goto _skip
endlocal
set /a count+=1
)
:_skip
echo !first!

for command is executed only for the first value when a label is inside

I have the script
for /f "delims=" %%i in ('dir "%folder%*.txt" /b /s') do (
set s=%%i
set s=!s:%folder%=!
set new_s=!s:\=!
if "x!new_s!" NEQ "x!s!" (
:ProcessListSource
For /f "tokens=1* delims=\" %%A in ("!s!") do (
if "%%A" NEQ "" (
if "!Folder1!" NEQ "" (
Set Folder1=!Folder1!\!Name!
)else (
Set Folder1=!Name!
)
Set Name=%%A
)
if "%%B" NEQ "" (
set s=%%B
goto :ProcessListSource
)
)
echo Folder is: !Folder1!
echo Name is: !Name!
echo ---------------------
) else (
echo Not a folder !s!
)
)
but it does not work as I would have expected:
The first for is executed only once and also the last echo is printed on the screen.
Given a folder I need the files from subfolders without the given folder and than split them into the folder and file
Ex: folder=C:\test
The for would give me the file C:\test\test1\test2\t.txt
And I need test1\test2 and t.txt
GOTO breaks your FOR /F \ IF context and they can be executed only once.
More simple example:
#echo off
for /l %%S in (1=1=5) do (
echo %%S
goto :inner_label
rem
:inner_label
rem
)
This will print only 1 . Do you really need the GOTO here?
When the parser reads your code, all the code inside your for loop is "considered" as only one command that is readed, parsed and executed. As stated in the npocmaka answer, any goto call takes you out of this "line" of code, ending the process of the for loop.
This is a alternative. Use pushd + xcopy /l /s commands to generate a list of the relative paths of the files.
#echo off
setlocal enableextensions disabledelayedexpansion
set "folder=%cd%"
pushd "%folder%"
for /f "delims=" %%a in ('xcopy /l /s /y * "%temp%"^|findstr /vbr /c:"[0-9]"'
) do for /f "delims=: tokens=1,*" %%b in ("%%~a") do (
echo [%%c] [%%~nxa]
)
popd

Resources