Windows batch script to collate log files into master log under condition - windows

I’m new to batch scripting and thanks to stackoverflow, I’m able to put together a script with no time
I’m working on a batch script which triggers other batch scriptlets in parallel (with combination of start + cmd) generating individual logs, wait for them to complete and collate them into a master log.
The condition i’m using is that every log file ends with a keyword “Elapsed” the master scripts checks if each log ends with the keyword and moves the log to the masterlog, else moves to a timeout state and repeats the process again. It work fine for the first attempts but fails read the last line of rest of the files (ch2.log ch3.log and ch4.log) and copies them without checking. Could you please let me know what I am missing?
Here is the part of the script which has the logic
for /f %%i in ('dir /b ch*.log') do (
REM display the list of logs (in this case it's ch1.log ch2.log ch3.log ch4.log)
set %fname% =%%i
:ctout
timeout 20>nul
REM wait until the timer runs out
for /f delims ^=^ eol^=%%l in (%fname%) do set lastline=%%l
REM check for the last line of the file and set the last line of the log as 'lastline'
echo %lastline% | findstr /i "\<Elapsed\>" >null && set var=elapsed
REM check if the lastline has the word "Elapsed", which marks the end of file and assign a dummy variable
if not "%var%"="elapsed" goto :ctout
REM check if the variable is "elapsed" else goto ctout
type %fname% >> masterlog.txt
REM if the condition satisfies the contents of ch1.log is moved to masterlog.txt
del /s %fname% >nul 2>nul
REM deletes the logs from the list and moves to the next log file
)

for /f %%i in ('dir /b ch*.log') do (
REM display the list of logs (in this case it's ch1.log ch2.log ch3.log ch4.log)
call :wait "%%i"
)
rem ... any remaining instructions after concatenating logs here
goto :eof
rem Jumps over the internal subroutine ":wait"
:wait
timeout 20>nul
REM wait until the timer runs out
for /f "usebackq" %%l in (%1) do set lastline=%%l
REM check for the last line of the file and set the last line of the log as 'lastline'
set "var="
echo %lastline% | findstr /i "\<Elapsed\>" >nul && set var=elapsed
REM check if the lastline has the word "Elapsed", which marks the end of file and assign a dummy variable
if not "%var%"=="elapsed" goto wait
REM check if the variable is "elapsed" else goto wait
>> masterlog.txt type %1
REM if the condition satisfies the contents of ch?.log is moved to masterlog.txt
del /s %1 >nul 2>nul
REM deletes the logs from the list and moves to the next log file
goto :eof
Note: Since the filename is being supplied to the :wait routine as a quoted string, %1 will be quoted hence requirement for usebackq.
eol and delims are not required as the default delims includes Space
set var to nothing before the test as its value will persist. the first file will set var to not-empty, so it needs t be cleared before the second file is tested. The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
the destination for findstr redirection should be nul, not null - you will find that a file named null was created.
The comparison operator in an if is == not =.

Related

Reading lines from a txt file into variables in batch

I am trying to figure out how to read IP addresses from a file named "IPList.txt) into individual variables in a batch script. Here's what I have so far.
:DEFINITIONS
set LOGFILE=IPScript.log
set IPLIST=C:\IPLIST.txt
echo Script Started >> %LOGFILE%
goto SetIP
:SetIP
for /f "tokens=*" %%a in (%IPLIST%) do (
set FirstIP=%%a
)
echo The first IP is %FirstIP% >> %LOGFILE%
exit
The output I'm getting in "IPscript.log" is "The First IP is: " with no IP listed, just a space. Also, is there a way for me to set multiple IPs like this, in just one for loop?
Here's a quick example to assist you:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"
Set "LOGFILE=IPScript.log"
:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
Echo The file %IPLIST% does not exist.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
) Else If "%%~aG" GEq "d" (
Echo Expected a file, but %IPLIST% is a directory.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="
:START_MAIN
Set "i=1000"
(
Echo Script Started
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
For %%H In ("!i:~-3!") Do (
EndLocal
Set "IP[%%~H]=%%G"
Echo IP[%%~H] is %%G
)
)
) 1> "%LOGFILE%"
:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
Echo %IPLIST% had no readable file content.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:VIEW_IP_VARIABLES
Set IP[
Pause & GoTo :EOF
If you have an existing %LOGFILE%, and you intend to append to it, (as opposed to overwrite/create one), change 1> "%LOGFILE%" to 1>> "%LOGFILE%".
If you didn't really need %LOGFILE%, e.g. it was used by you just for testing, it would look a little more like this:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"
:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
Echo The file %IPLIST% does not exist.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
) Else If "%%~aG" GEq "d" (
Echo Expected a file, but %IPLIST% is a directory.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="
:START_MAIN
Set "i=1000"
Echo Script Started
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
For %%H In ("!i:~-3!") Do (
EndLocal
Set "IP[%%~H]=%%G"
)
)
:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
Echo %IPLIST% had no readable file content.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:VIEW_IP_VARIABLES
Set IP[
Pause & GoTo :EOF
The last line in both examples is for display purposes. If you're testing/running this script from within cmd.exe, you may omit it.
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r ".*" "%filename1%"') DO set "IP%%a=%%b"
)
set IP
findstr reads the file in filename1 and produces a list of the format n:content of line n.
The for /f reads this list, and partitions it using 2 tokens - %%a gets the first token (1) and %%b the remainder of the line (*) using : as a delimiter.
So simply set the IP variables from there.
set ip displays all variables that start ip
Probability is that your file contains empty line(s) after the last IP. Your original code would have reported the LAST IP, not the FIRST as the value in firstip is overwritten on each iteration, so it would be cleared by being set to nothing when the empty lines are read.
The solution above would simply execute (eg) set "IP6=" under these circumstances, clearing the variable.
You could have obtained the first IP by using
if not defined firstip set "FirstIP=%%a"
I'm assuming a clean environment here - that is, that each batch you run includes a setlocal after the #echo off (which restores the initial environment when the batch finishes) and the variables used are known-empty.
Bonus:
changing the set command to
set "IP%%a=%%b"&if "%%b" neq "" set "ipmax=%%a"
would set ipmax to the number of the last non-empty line, as %%b is empty for an empty line.
The batch file could have following command lines:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=%~dp0IPScript.log"
set "IPLIST=%~dp0IPLIST.txt"
set "AddressCount=0"
echo Script started>"%LOGFILE%"
for /F "delims==" %%I in ('set IP_Address_ 2^>nul') do set "%%I="
if exist "%IPLIST%" for /F "useback delims=" %%I in ("%IPLIST%") do (
set /A AddressCount+=1
call set "IP_Address_%%AddressCount%%=%%I"
)
if not %AddressCount% == 0 (
if %AddressCount% == 1 (
echo The IP address is:
) else echo The IP addresses are:
echo/
set IP_Address_
) >>"%LOGFILE%"
endlocal
The batch file first two command line define the execution environment which means:
Disable command echo mode.
Push current command extension state on stack and enable command extensions.
Push current delayed expansion state on stack and disable delayed environment variable expansion.
Push path of current directory on stack.
Push pointer to current list of environment variables on stack and create a copy of the entire current environment variables list to use next.
The third and fourth line define two environment variables with the name of the log file and the name of the IP address list file with full qualified file name. The file path of both files is defined as path of the directory containing the batch file referenced with %~dp0. This path always ends with \ and for that reason no additional backslash is needed on concatenating this path with the two file names.
The fifth line define the environment variable AddressCount with value 0.
The sixth line creates the log file in current directory with overwriting an already existing log file. There is no space left to redirection operator > as this space would be output by command ECHO and therefore written as trailing space also into the log file.
The first FOR command with option /F starts in background with %ComSpec% /c one more command process with the command line between ' appended as additional arguments. So executed is in background with Windows installed into C:\Windows:
C:\Windows\System32\cmd.exe /c set IP_Address_ 2>nul
Windows creates a copy of current list of environment variables for the command process started in background. The background command process runs command SET to output all environment variables with name, an equal sign and the string value assigned to the variable line by line of which name starts with IP_Address_. This output to handle STDOUT of background command process is captured by FOR respectively the command process which is processing the batch file. The error message output by SET on no environment variable define with a name starting with IP_Address_ is redirected from handle STDERR to device NUL to suppress this error message.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR processes the captured output line by line after started background command process closed itself after execution of command SET. Empty lines are always ignored by FOR which can be ignored as there are no empty lines output by SET.
FOR would split up by default the current line into substrings using normal space and horizontal tab as delimiters. This default line splitting behavior is not wanted here. The option delims== defines the equal sign as string delimiter to split the line on = which is the character between variable name and variable value.
FOR would next ignore the line if the first substring would start with a semicolon which is the default end of line character. The command SET outputs only lines starting with IP_Address_ and for that reason the default eol=; can be kept in this case.
FOR assigns just the first substring to the specified loop variable I as tokens=1 is the default. That is exactly the wanted behavior in this case.
So FOR assigns one environment variable name starting with IP_Address_ to loop variable I and runs next the command SET to delete this environment variable in current list of environment variables of command process processing the batch file.
In other words the first FOR is for deletion of all environment variables of which name starts with IP_Address_ defined by chance outside the batch file.
The next line first checks if the file with the list of environment variables exists at all in directory of the batch file. In this case once again FOR is used to process lines, but this time read line by line from the specified list file instead of captured output of a background command process. The usage of " instead of ' with the option usebackq makes the difference.
There is used the option delims= to define an empty list of delimiters resulting in getting each non-empty line not starting with ; assigned completely to the specified loop variable I.
For each string assigned to loop variable I the current value of environment variable AddressCount is incremented by one using an arithmetic expression evaluated by command SET.
This value is used on next command line to define an environment variable of which name starts with IP_Address_ and has appended the current address count value with line read from file assigned to the environment variable.
There is usually used delayed expansion for such tasks on which the second command line in command block of second FOR loop would be:
set "IP_Address_!AddressCount!=%%I"
But the code above uses the alternative method with command call to parse set "IP_Address_%%AddressCount%%=%%I" a second time which was already modified to set "IP_Address_%AddressCount%=%I" before the IF condition left to FOR was executed at all.
The next IF condition checks if any line was read from the list file with the IP addresses. In this case first an information line is output depending on having read exactly one line from the file or more than one line. Then an empty line is output and last all environment variables of which name starts with IP_Address_ with = and the line (IP address) assigned to the environment variable. All this output is appended to the log file.
The last command restores previous execution environment which means:
Discard the current list of environment variables and pop from stack the pointer to initial list of environment variables resulting in restoring the initial list of environment variables. In other words all environment variables defined or modified by the batch file after command SETLOCAL in second command line are lost forever.
Pop path of current directory from stack and make this directory again the current directory. The current directory between setlocal and endlocal was not changed by the code between and so this does not matter here.
Pop delayed expansion state from stack and enable or disable delayed environment variable expansion accordingly to restore initial delayed expansion behavior.
Pop current command extension state from stack and enable or disable command extensions accordingly to restore initial command extension behavior.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
See also:
Variables are not behaving as expected
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Microsoft's documentation for the Windows Commands
SS64.com - A-Z index of Windows CMD commands

How to extract certain portion of a file that starts with "HDR" followed by a search keyword?

How to extract portion of file that starts with HDR followed by search keyword using a batch file and Windows command interpreter?
Only certain HDR should be copied to another file with name GoodHDR.txt.
HDRs not included in searches should be copied also to another file with name BadHDR.txt.
For example, I have HeaderList.txt below and need to get HEADER0001 and HEADER0003 portions.
HDRHEADER0001 X004010850P
BEG00SAD202659801032017021699CANE
HDRHEADER0002 X004010850P
BEG00SAD202611701012017021499CANW
DTM01020170214
N1ST 92 0642397236
N315829 RUE BELLERIVE
N4MONTREAL QCH1A5A6 CANADA
HDRHEADER0003 X004010850P
BEG00SAP521006901012017021399CANOUT B16885
DTM01020170213
N1STCEGEP SAINT LAURENT 92 0642385892
Expected outcome:
GoodHDR.txt only contains HEADER0001 and HEADER0003.
HDRHEADER0001 X004010850P
BEG00SAD202659801032017021699CANE
HDRHEADER0003 X004010850P
BEG00SAP521006901012017021399CANOUT B16885
DTM01020170213
N1STCEGEP SAINT LAURENT 92 0642385892
BadHDR.txt contains HEADER0002:
HDRHEADER0002 X004010850P
BEG00SAD202611701012017021499CANW
DTM01020170214
N1ST 92 0642397236
N315829 RUE BELLERIVE
N4MONTREAL
The batch code below expects to be started with the parameters 0001 0003 to produce the two output files from source file as posted in question.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFile=HeaderList.txt"
set "FoundFile=GoodHDR.txt"
set "IgnoreFile=BadHDR.txt"
if "%~1" == "" goto ShowHelp
if "%~1" == "/?" goto ShowHelp
if not exist "%SourceFile%" goto NoHeaderList
del "%IgnoreFile%" 2>nul
del "%FoundFile%" 2>nul
rem Assign the headers passed as arguments to environment variables with
rem name HDR%~1X, HDR%~2X, HDR%~3X, etc. used later for quickly searching
rem for number of current header within the list of specified numbers.
rem All parameter strings not existing of exactly 4 digits are ignored.
set HeadersCount=0
:SetHeaders
set "HeaderNumber=%~1"
if "%HeaderNumber:~3,1%" == "" goto NextArgument
if not "%HeaderNumber:~4,1%" == "" goto NextArgument
for /F "delims=0123456789" %%I in ("%HeaderNumber%") do goto NextArgument
set "HDR%HeaderNumber%X=%HeaderNumber%"
set /A HeadersCount+=1
:NextArgument
shift /1
if not "%~1" == "" goto SetHeaders
if %HeadersCount% == 0 goto ShowHelp
rem Proces the header blocks in the source file.
set "OutputFile=%IgnoreFile%"
for /F "usebackq delims=" %%L in ("%SourceFile%") do call :ProcessLine "%%L"
rem Output a summary information of header block separation process.
if "%HeadersCount%" == "-1" set "HeadersCount="
if not defined HeadersCount (
echo All header blocks found and written to file "%FoundFile%".
goto EndBatch
)
set "SingularPlural= was"
if not %HeadersCount% == 1 set "SingularPlural=s were"
echo Following header block%SingularPlural% not found:
echo/
for /F "tokens=2 delims==" %%V in ('set HDR') do echo %%V
goto EndBatch
rem ProcessLine is a subroutine called from main FOR loop with
rem a line read from source file as first and only parameter.
rem It compares the beginning of the line with HDRHEADER. The line is
rem written to active output file if it does not start with that string.
rem Otherwise the string after HDRHEADER is extracted from the
rem line and searched in list of HDR environment variables.
rem Is the header in list of environment variables, this line and all
rem following lines up to next header line or end of source file are
rem written to file with found header blocks.
rem Otherwise the current header line and all following lines up to
rem next header line or end of source file are written to file with
rem header blocks to ignore.
rem Once all header blocks to find are indeed found and written completely
rem to the file for found header blocks, all remaining lines of source file
rem are written to the ignore file without further evaluation.
:ProcessLine
if not defined HeadersCount (
>>"%OutputFile%" echo %~1
goto :EOF
)
set "Line=%~1"
if not "%Line:~0,9%" == "HDRHEADER" (
>>"%OutputFile%" echo %~1
goto :EOF
)
set "HeaderLine=%Line:~9%"
for /F %%N in ("%HeaderLine%") do set "HeaderNumber=%%N"
set "OutputFile=%IgnoreFile%"
for /F %%N in ('set HDR%HeaderNumber%X 2^>nul') do (
set "HDR%HeaderNumber%X="
set /A HeadersCount-=1
set "OutputFile=%FoundFile%"
)
>>"%OutputFile%" echo %~1
if %HeadersCount% == 0 (
set "HeadersCount=-1"
) else if %HeadersCount% == -1 (
set "HeadersCount="
)
goto :EOF
:NoHeaderList
echo Error: The file "%SourceFile%" could not be not found in directory:
echo/
echo %CD%
goto EndBatch
:ShowHelp
echo Searches for specified headers in "%SourceFile%" and writes the
echo found header blocks to file "%FoundFile%" and all other to file
echo "%IgnoreFile%" and outputs the header blocks not found in file.
echo/
echo %~n0 XXXX [YYYY] [ZZZZ] [...]
echo/
echo %~nx0 must be called with at least one header number.
echo Only numbers with 4 digits are accepted as parameters.
:EndBatch
echo/
endlocal
pause
The redirection operator >> and the current name of the output file is specified at beginning of all lines which print with command ECHO the current line to avoid appending a trailing space on each line written to an output file and get the line printing nevertheless working if a line ends with 1, 2, 3, ...
Some additional notes about limitations on usage of this code:
The batch code is written with avoiding the usage of delayed expansion to be able to easily process also lines containing an exclamation mark. The disadvantage of not using delayed expansion is that lines containing characters in a line with a special meaning on command line like &, >, <, |, etc. result in wrong output and can even produce additional, unwanted files in current directory.It would be of course possible to extend the batch code to work also for lines in source file containing any ANSI character, but this is not necessary according to source file example which does not contain any "poison" character.
FOR ignores empty lines on reading lines from a text file. So the code as is produces 1 or 2 output files with no empty lines copied from source file.
The main FOR loop reading the lines from source file skips all lines starting with a semicolon. If this could be a problem, specify on FOR command line reading the lines from source file before delims= the parameter eol= with a character which definitely never exists at beginning of a line in source file. See help of command FOR displayed on running in a command prompt window for /? for details on parameters of set /F like eol=, delims= and tokens=.
The length of a string assigned to an environment variable plus equal sign plus name of environment variable is limited to 8192 characters. For that reason this batch code can't be used for a source file with lines longer than 8187 characters.
The length of a command line is also limited. The maximum length depends on version of Windows. So this batch file can't be used with a very large number of header numbers.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
shift /?
Read also the Microsoft article about Using Command Redirection Operators for details about >> and 2>nul and 2^>nul with redirection operator > being escaped with caret character ^ for being interpreted as literal character on parsing FOR command line, but as redirection operator later on execution of command SET by command FOR.

execute batch files in parallel and get exit code from each

How can I execute set of batch files from single batch file in parallel and get the exit code from each. When I use start it executes the batch file in parallel (new cmd window) but don't return the exit code from each. And while using call, I can get the exit code but the batch file execution happens sequentially. I have following code:
ECHO ON
setlocal EnableDelayedExpansion
sqlcmd -S server_name -E -i select_code.sql >\path\output.txt
for /f "skip=2 tokens=1-3 delims= " %%a in ('findstr /v /c:"-" \path\output.txt') do (
echo $$src=%%a>\path1\%%c.prm
echo $$trg=%%b>>\path1\%%c.prm
set param_name=%%c
start cmd \k \path\exec_pmcmd_ctrm.bat workflow_name %%param_name%%
ping 1.1.1.1 -n 1 -w 5000 > nul
set exitcode="%V_EXITCODE%"
echo %exitcode%>>\path\exitcode.txt
)
This executes the exec_pmcmd_ctrm.bat 3 times with different variable in parallel , but I am unable to get the exit code from each execution. I tried using call but then I miss the parallel execution of bat file. Any help in this regard?
First of all, the "exit code" from another Batch file (that ends with exit /B value command) is taken via %ERRORLEVEL% variable (not %V_EXITCODE%). Also, if such a value changes inside a FOR loop, it must be taken via Delayed Expansion instead: !ERRORLEVEL! (and EnableDelayedExpansion at beginning of your program). However, these points don't solve your problem because there is a misconception here...
When START command is used (without the /WAIT switch), a parallel cmd.exe process start execution. This means that there is not a direct way that the first Batch file could know in which moment the parallel Batch ends in order to get its ERRORLEVEL at that point! There is not a "wait for a started Batch file" command, but even if it would exist, it don't solve the problem of have several concurrent Batch files. In other words, your problem can not be solved via direct commands, so a work around is necessary.
The simplest solution is that the parallel Batch files store their ERRORLEVEL values in a file that could be later read by the original Batch file. Doing that imply a synchronization problem in order to avoid simultaneous write access to the same file, but that is another story...
Here is a pure batch-file solution. Basically, this script executes all batch files located in the same directory simultaneously, where the exit code (ErrorLevel) of each one is written to an individual log file (with the same name as the batch file and extension .log); these files are checked for existence; as soon as such a log file is found, the stored exit code is read and copied into a summary log file, together with the respective batch file name; as soon as all log files have been processed, this script is terminated; the exit code of this script is zero only if all the exit codes of the executed batch files are zero too. So here is the code -- see all the explanatory rem remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Collect available scripts in an array:
set /A "INDEX=0"
for %%J in ("%~dp0*.bat" "%~dp0*.cmd") do (
if /I not "%%~nxJ"=="%~nx0" (
set "ITEM=%%~fJ"
call set "$BATCH[%%INDEX%%]=%%ITEM%%"
set /A "INDEX+=1"
)
)
rem // Execute scripts simultaneously, write exit codes to individual log files:
for /F "tokens=1,* delims==" %%I in ('set $BATCH[') do (
start "" /MIN cmd /C rem/ ^& "%%~fJ" ^& ^> "%%~dpnJ.log" call echo %%^^ErrorLevel%%
)
rem // Deplete summary log file:
> "%~dpn0.log" rem/
rem // Polling loop to check whether individual log files are available:
:POLLING
rem // Give processor some idle time:
> nul timeout /T 1 /NOBREAK
rem /* Loop through all available array elements; for every accomplished script,
rem so its log file is availabe, the related array element becomes deleted,
rem so finally, there should not be any more array elements defined: */
for /F "tokens=1,* delims==" %%I in ('set $BATCH[') do (
rem // Suppress error message in case log file is not yet available:
2> nul (
rem // Read exid code from log file:
set "ERRLEV=" & set "FILE="
< "%%~dpnJ.log" set /P ERRLEV=""
if defined ERRLEV (
rem // Copy the read exit code to the summary log file:
set "NAME=%%~nxJ"
>> "%~dpn0.log" call echo(%%ERRLEV%% "%%NAME%%"
rem // Undefine related array element:
set "%%I="
rem // Store log file path for later deletion:
set "FILE=%%~dpnJ.log"
)
rem // Delete individual log file finally:
if defined FILE call del "%%FILE%%"
)
)
rem // Jump to polling loop in case there are still array elements:
> nul 2>&1 set $BATCH[ && goto :POLLING
rem // Check individual exit codes and return first non-zero value, if any:
set "ERRALL="
for /F "usebackq" %%I in ("%~dpn0.log") do (
if not defined ERRALL if %%I neq 0 set "ERRALL=%%I"
)
if not defined ERRALL set "ERRALL=0"
endlocal & exit /B %ERRALL%
This approach is quite similar to the one I used in the following Improving Batch File for loop with start subcommand, but there the outputs of simultaneously executed commands are collected.

.bat fails - where is the problem

What could cause running a batch file to terminate in the middle of running?
An exit statement will cause a batch file to terminate early.
There exists some immediatly stop errors.
But in your special case, the cause is unknown, like the code.
rem error1 - Invalid % Expansion
rem %~
rem error2 - call REM /?
set "switch=/?"
call rem %%switch%%
rem error3 - stackoverflow
:stack
call :stack
rem error4 - call caret crash only with Vista
set "caret=^"
call echo %%caret%%
rem error5 - Long expansion
rem shows that expansion of % in a line is before the rem ignores the rest of the line
rem The expansion results in a very long line with 10000 characters, the batch stops immediatly
setlocal EnableDelayedExpansion
set "longStr=."
for /L %%c in (1,1,13) DO set longStr=!longStr:~0,2500!!longStr:~0,2500!
rem Now we heave a string with 5000 characters, and build a rem line with 2 times 5000 = 10000 characters
rem The next line crashes the batch file
rem tokenOne %longStr% %longStr%
(
ENDLOCAL
goto :eof
)
rem error 6 - %%var<LF> error, crashes only sometimes
set critical_content=hello%%~^
echo $
for %%a in (1 ) do (
for %%x in (4) do (
rem #%critical_content%#
)
)
As others mentioned, this is too open-ended to answer effectively.
Nevertheless, you may want to see this question about needing the CALL command to call another batch script and have it return to the call site.

batch file to return next to last line of text file

I have a file that contains the output of a file compare thats written to a text file:
Comparing files C:\LOGS\old.txt and C:\LOGS\NEW.TXT
***** C:\LOGS\old.txt
***** C:\LOGS\NEW.TXT
folder_thats_different
*****
I need to pull out the next to last line "folder_thats_different" and put in a new string:
folder contains a file that is different: folder_thats_different
Yes, I know I can use another language, but I'm stuck with batch files for now.
You can try to read it with a for-loop and take the current line, and always save the previous line
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%x in (myFile.txt) do (
set "previous=!last!"
set "last=%%x"
)
echo !previous!
Here's an example you can use as a starting point. Just change the filename in the set command= line to the appropriate name (or replace the command with whatever will gerneate the log listing).
#echo off
#setlocal
(set command=type test.txt)
for /f "usebackq tokens=*" %%i in (`%command%`) do call :process_line %%i
echo next to last line: %old_line%
goto :eof
:process_line
(set old_line=%new_line%)
(set new_line=%*)
goto :eof
Of course, you'll probably want to do something other than simply echoing the found line.
The first answer works for me. I also added 2 lines after the end to allow it to repeat so I could watch an active log file without having to close and reopen it. I do a lot of debugging for the mods that are used in the game Space Engineers.
My version looks like this:
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%x in (SpaceEngineers.log) do (
set "previous=!last!"
set "last=%%x"
)
echo !previous!
timeout 15 /nobreak
se_log
The line below stops the batch file from looping too fast and stop the key bypass. To change the amount of time in seconds just change the number "15" to anything you want. To stop the batch file just press ctrl+c.
timeout 15 /nobreak
The line below is the name of the batch file I made so it will tell CMD to run this again.
se_log

Resources