Multiple instances of a RegEx string in FINDSTR - regex

I am looking for a way in a Windows batch file to find if a file contains the word Shutdown a given number of times.
I have tried this using FINDSTR:
FINDSTR /r .*Shutdown.*{5} file.txt
This doesn't seem to work, but if I remove the {5} it executes successfully.
Also I would eventually like the 5 to be a variable, presumably this is possible with some sort of dynamic command?
Thanks

It is possible to use FINDSTR to detect if "Shutdown" appears 5 or more times anywhere within a file. All the information you need is contained within What are the undocumented features and limitations of the Windows FINDSTR command?, but it is more than a bit tricky and not at all obvious.
A regex search can match multiple instances of Shutdown across multiple lines, as long as all intervening characters, including carriage return and linefeed characters, are matched by an appropriate character class range. I cannot post the characters required here, so I will use symbolic notation within angle brackets.
The following regex expression will match any byte character except 0xFF (decimal 255). It consists of a character class expression with two ranges and a gap in the middle for 0xFF. The gap is critical because FINDSTR will fail (and possibly hang) if 0xFF is included:
[<0x01>-<space><tab>-<0xEA>]
You might think the expression should be [<0x01>-<0xFE>], but that does not work because FINDSTR does not collate characters by the numeric code value.
So to look for 5 or more instances of Shutdown anywhere within the file, you would need the following regex search:
Shutdown[<0x01>-<space><tab>-<0xEA>]*Shutdown[<0x01>-<space><tab>-<0xEA>]*Shutdown[<0x01>-<space><tab>-<0xEA>]*Shutdown[<0x01>-<space><tab>-<0xEA>]*Shutdown
The 0xEA (decimal 234) character is an extended ASCII character, and extended ASCII cannot be included on the command line of a FINDSTR search. So the search string must be put in an external file and the /G:file option must be used.
Here is a complete batch script that takes the minimum number of Shutdown instances to search for as the 1st argument, and the name of the file to search as the 2nd argument. Again I use symbolic notation within angle brackets in place of the actual characters needed.
#echo off
set count=%1
set file=%2
setlocal enableDelayedExpansion
set "search="
for /l %%N in (1 1 %count%) do set "search=!search!Shutdown[<0x01>-<space><tab>-<0xEA>]*"
set "search=!search:~0,-9!"
echo(!search!>search.txt
findstr /rg:search.txt %file% >nul&&echo FOUND||echo NOT found
The maximum supported count is limited by the maximum regex string length. For XP the max regex length is 127 bytes, equating to a count of 7. On Vista and Windows 7 the max regex length is 254 bytes, which should support a count of 15. But my testing on Windows 7 only supported a count up to 12. Additional tests reveal the max length is affected by how many string literals and character classes appear, as well as the relative placement of each. But I haven't been able to figure out an exact formula.
If you don't want to use an external file, then the following regex expression is almost as good. It matches any characters except for the following extended ASCII hex codes: 0xE0, 0xE2, 0xE3, 0xE4, 0xE5, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xEE, 0xFF.
[<0x01>-<space><tab>-Z]
The full regex search would be:
Shutdown[<0x01>-<space><tab>-Z]*Shutdown[<0x01>-<space><tab>-Z]*Shutdown[<0x01>-<space><tab>-Z]*Shutdown[<0x01>-<space><tab>-Z]*Shutdown
And here is the complete batch script:
#echo off
setlocal enableDelayedExpansion
set count=%1
set file=%2
set "search="
for /l %%N in (1 1 %count%) do set "search=!search!Shutdown[<0x01>-<space><tab>-Z]*"
set "search=!search:~0,-9!"
findstr /rc:"!search!" %file% >nul&&echo FOUND||echo NOT found

.*Shutdown.*Shutdown.*Shutdown.*Shutdown.*Shutdown.*
would seem to be the only way for FINDSTR
srsly.
Test batch:
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%i IN (f5shutdown.txt) DO (
ECHO %%i|FINDSTR /r .*Shutdown.*Shutdown.*Shutdown.*Shutdown.*Shutdown.* >NUL
IF ERRORLEVEL 1 ECHO FAILED IN "%%i"
IF NOT ERRORLEVEL 1 ECHO FOUND IN "%%i"
)
GOTO :eof
Test textfile in f5shutdown.txt
Shutdown
Shutdown Shutdown
Shutdown Shutdown Shutdown Shutdown
Shutdown Shutdown Shutdown Shutdown Shutdown
Shutdown already !Shutdown Shutdown Shutdown Shutdown now
You should now Shutdown Shutdown Shutdown Shutdown Shutdown
ShutdownShutdownShutdownShutdownShutdown
OK, don't shutdown then...
Result:
FAILED IN "Shutdown"
FAILED IN "Shutdown Shutdown"
FAILED IN "Shutdown Shutdown Shutdown Shutdown"
FOUND IN "Shutdown Shutdown Shutdown Shutdown Shutdown"
FOUND IN "Shutdown already !Shutdown Shutdown Shutdown Shutdown now"
FOUND IN "You should now Shutdown Shutdown Shutdown Shutdown Shutdown"
FOUND IN "ShutdownShutdownShutdownShutdownShutdown"
FAILED IN "OK, don't shutdown then..."
Works for me (W7)
Ah - perhaps you meant "occurs on 5 (or whatever) lines or "occurs 5 times in the file"...
#ECHO OFF
SETLOCAL
FOR /f "delims=" %%i IN (f5shutdown.txt) DO (
ECHO %%i|FINDSTR /r .*Shutdown.*Shutdown.*Shutdown.*Shutdown.*Shutdown.* >NUL
IF ERRORLEVEL 1 ECHO FAILED IN "%%i"
IF NOT ERRORLEVEL 1 ECHO FOUND IN "%%i"
)
:: For 'shutdown' on n different lines: (assuming case-insensitive)
FOR /f %%i IN ('FIND /i /c "shutdown" ^<f5shutdown.txt') DO SET shutdowns=%%i
ECHO Second method: %shutdowns% lines found with "Shutdown"
:: For 'shutdown' occurrences (assuming case-insensitive)
SET /a shutdowns=0
FOR /f "delims=" %%i IN (f5shutdown.txt) DO (
SET line=%%i
CALL :findem
)
ECHO Third method found "Shutdown" %shutdowns% times
GOTO :eof
:findem
SET line=%line:"=%
SET after=%line:*shutdown=%
IF NOT DEFINED after SET /a shutdowns+=1&GOTO :EOF
if NOT "%line%"=="%after%" SET "line=%after%"&SET /a shutdowns+=1&GOTO findem
GOTO :eof
(I added an extra non-shutdown-containing line for the test. Found 8 lines and 28 occurrences)

This should detect when shutdown is on only 5 lines in the file.txt
#echo off
for /f %%a in ('find /i /c "shutdown" ^< "file.txt" ') do if %%a EQU 5 echo found it 5 times

Related

Batch File..( was unexpected at this time [duplicate]

This question already has answers here:
Variables are not behaving as expected
(1 answer)
Example of delayed expansion in batch file
(5 answers)
Closed 2 years ago.
I have developed a batch file that is designed to check a value in a given excel sheet.
And decides what to do depending of the value in the Excel sheet (value either 0 or more than zero)
Code is as follows
#echo off
setlocal EnableDelayedExpansion
cd\
D:
cscript //nologo testexcel.vbs
IF !ERRORLEVEL! EQU 1 (
taskkill /F /IM explorer.exe
*****MORE ACTIONS HERE*****
SET running=0
FOR /f "tokens=*" %%A IN ('tasklist^ /v^| findstr /i /c:"iexplore.exe"') DO SET running=1
IF %running% == 0 (
explorer.exe
)
) ELSE (
***OTHER ACTIONS HERE***
)
exit
The code will run a VBscript using the cscript command and will; return a ERROR LEVEL value, either ERROR LEVEL is 1 or otherwise. For some reason I keep getting the below error:
( was unexpected at this time.
I understand that this may be happening due to the due to all the commands and variables within those parentheses are expanded. But I am using setlocal EnableDelayedExpansion but still getting the same error.

batch file IF condition

I'm trying to get the current active connection with netsh (i'd rather not use wmic) using the following code.
#echo off
FOR /F "tokens=3,*" %%A IN ('netsh interface show interface^|findstr /i "\<connected\>"') DO (
if %%B == .........
)
I only want to account for the default connection names:
Wi-Fi, Ethernet, Wireless Network Connection, Local Area Connection and do something based on that.
eg: if %%B = WiFi or Ethernet or....(
::do something here
)
I only want "something" to be executed once because only 1 of those connections will ever be active at any given time.
I think something like this ought to do the trick:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "tokens=3,*" %%a in ('netsh interface show interface ^| findstr /i "\<connected\>"') do (
set ifname=%%b
if "!ifname:~0,8!"=="Ethernet" goto execute
if "!ifname:~0,21!"=="Local Area Connection" goto execute
if "!ifname:~0,5!"=="Wi-Fi" goto execute
if "!ifname:~0,27!"=="Wireless Network Connection" goto execute
)
echo No connected interface found, exiting...
goto :EOF
:execute
echo Found connected interface (%ifname%), executing...
:: do whatever you need
Basically the idea is to look for interfaces that begin with those four different strings, then jump to the label that executes whatever it is you want.
[I'd be remiss if I didn't point out that this is likely better to be done in PowerShell nowadays, but…]
Here's an untested example, which is supposed to just tell you the interface name of the device associated with your only assigned IP Address.
#Echo Off
SetLocal EnableExtensions
For /F "Delims==" %%G In ('"(Set Interface) 2> NUL"') Do Set "%%G="
For /F Tokens^=4 %%G In ('%__APPDIR__%ROUTE.EXE -4 PRINT 0.0.0.0 ^
^| %__APPDIR__%findstr.exe /RC:"0.0.0.0 *0.0.0.0"') Do (Set "InterfaceIP=%%G"
For /F Tokens^=5* %%H In ('%__APPDIR__%netsh.exe interface IP show route^
^|%__APPDIR__%find.exe "%%G"') Do Set "InterfaceName=%%I")
If Not Defined InterfaceIP GoTo :EOF
Set Interface & Pause
[EDIT /]
Here's a slightly modified version of the example from my comments which uses ping.exe instead of ROUTE.EXE for returning the IP Address used to determine the Interface Name.
#Echo Off
SetLocal EnableExtensions
For /F "Delims==" %%G In ('"(Set Interface) 2> NUL"') Do Set "%%G="
For /F "Tokens=2 Delims=[]" %%G In ('^"%__APPDIR__%ping.exe "%COMPUTERNAME%" ^
-n 1 -4 2^> NUL ^| %__APPDIR__%find.exe /I "%COMPUTERNAME%"^"') Do (
Set "InterfaceIP=%%G" & For /F Tokens^=5* %%H In ('%__APPDIR__%netsh.exe ^
interface IP show route ^| %__APPDIR__%find.exe "%%G"'
) Do Set "InterfaceName=%%I")
If Not Defined InterfaceIP GoTo :EOF
Set Interface & Pause
The last line in both examples just displays the two variables so that you can see them. You'd obviously replace those with the rest of your code.

Parsing KBs from systeminfo in batch

Question:
What is a method that can parse out just the hotfix KB entries in a Windows systeminfo command report.
Background:
I have many windows systems that I need to get the list of installed KBs from. I can't install any software on the systems, so I've resolved to use a batch file.
I would like to have a list of KBs as the output, saved to a file " .txt"
My Current Method:
So far, I can get the full list from systeminfo and put the entire output into a text file named with the device's host name and date and stick it in a directory called Systeminfo Saves. That works well enough for me, but I'm really only interested in the KBs and I am hoping to use that output to compare it to a list of validated KBs so I can get a list of KBs that need installed.
I've been messing around with for statements and tokens and delimiters to narrow down the list to just the KB files, but without success.
Here's my current code:
#echo off
REM Get date and host and set it to sFileName
REM -----------------------------------------
setlocal enabledelayedexpansion
set ssDate=%date%
set sHost=%computername%
set sDate=!ssDate:/=-!
REM Remove the day of the week
set sDate=%sDate:~-10%
set sFileName=%sHost% %sDate%.txt
REM -----------------------------------------
REM - Save system info to hostname and date -
REM -----------------------------------------
systeminfo > "Systeminfo Saves/%sFileName%"
REM
REM PUT FOR STATEMENT HERE FOR OUTPUT OF JUST KBs
REM -----------------------------------------
My next step that I haven't been able to pull off is to parse out the installed KBs into a file.
For statements have thrown me for a loop (pun mildly intended). I've tried this:
REM OUTPUT a list of KBs
SET count=1
set lookieloo = "]: KB"
FOR /F "tokens=* USEBACKQ" %%F IN (`systeminfo`) DO (
SET var!count!=%%F
SET /a count=!count!+1
REM See if "]: KB" is in the resultant string
if not x%%F:"]: KB"=%==x%%F echo %%F >> "Systeminfo Saves/%sFileName%"
)
I'm splicing together answers that I don't fully understand from other Stack Overflow answers that partially answer my question.
The last save to file I'm fairly sure won't work
.
The "]: KB" doesn't work at all. I tried using the variable lookieloo to get that statement to work. I didn't get an error, but I didn't get a list of KBs either.
So, the question is: What would a batch program look like to export a text file of installed KBs on a system using the systeminfo command?
From LotPings comment, I was able to put together a full program that extracts the installed KBs using just batch.
#setlocal enableextensions enabledelayedexpansion
#echo off
REM Get date and host and set it to sFileName
REM -----------------------------------------
set ssDate=%date%
set sHost=%computername%
REM Get the directory and make it if it doesn't exist
set sDir=Systeminfo Saves
if not exist "%sDir%\" mkdir "%sDir%\"
set sDate=!ssDate:/=-!
REM Remove the day of the week
set sDate=%sDate:~-10%
REM Compile the full file name with directory
set sFileName=%sDir%^\%sHost% %sDate%.txt
REM -----------------------------------------
echo %sFileName%
REM -- Extract KB information and save to file --
(For /f "tokens=2delims=: " %%A in ('systeminfo^|findstr "\[[0-9][0-9]*\]:.KB[0-
9]*"') Do #Echo:%%A)>"%sFileName%"
REM ---------------------------------------------
Loading and parsing SystemInfo is a time consuming operation and whilst determining the updates can take a while on its own, using WMI is probably a quicker method.
Here's an alternative which uses WMIC:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "sDir=WMIC Saves"
Set "sDate=%DATE:/=-%"
Set "sDate=%sDate:~-10%"
Set "sFileName=%sDir%\%COMPUTERNAME% %sDate%.txt"
If Not Exist "%sDir%\" MD "%sDir%" 2>Nul || Exit /B
(For /F "EOL=H Tokens=*" %%A In ('"WMIC QFE Get HotFixID|Sort"'
) Do For /F "Tokens=*" %%B In ("%%A") Do Echo=%%B)>"%sFileName%"

Using batch to read a certain section of a file and create a variable

I've read a lot of things and I can't find away of doing this that I understand. This might be because I'm a wee newbie though.
What I'm trying to do is:
I have a file called start.txt with the contents
..\dll | toapp.exe "..\posdata\filename.xml" "..\OUT" "..\TEMP"
I want to read the contents of that in a batchfile and take only filename.xml as a variable. From device to device inside the network it changes.
I'm then going to use that variable to copy a file from 1 machine to another but I only want that one file and I can't be sure what it's called without looking in the start.txt. I can do all the copy and checks to make sure it's looking in the correct places but just not the findstr section.
Any idea of what to do to understand would be fantastic
#echo off
set "start_file=start.txt"
for /f tokens^=5^ delims^=\^" %%# in ('type "%start_file%"^|find /i "..\posdata"') do set "xml_file=%%#"
echo %xml_file%
Your question is a little bit unclear.Is this the only line in the file? Is this the exact content? How can I recognize the line with the file?
The script above will work in case there's only one line wwith "..\postdata" string and the content is exact as described in question.
Edit Covering the both cases:
#echo off
setlocal
set "start_file=start.txt"
set "xml_file="
for /f tokens^=5^ delims^=\^" %%# in ('type "%start_file%"^|find /i "..\posdata"^|find /i /v "-storedbpath"') do set "xml_file=%%#"
if not defined xml_file (
for /f "usebackq tokens=9" %%# in ("%start_file%") do (
set "xml_file=%%~#"
)
)
echo %xml_file%
Text this:
for /f "usebackq tokens=4" %%a in ("start.txt") do echo %%~nxa

How to execute a unit test several times in a single test run?

One of my tests is intermittently failing. I now want to execute the test frequently.
I wrote a little batch file
:loop
mstest /testcontainer:"path/test.dll" /test:"mytest"
set /a COUNT=%COUNT%+1
if %COUNT GTR %MAX% goto end
goto loop
While this works fine, though the output isn't meaningful or telling. Let's assume I run this test 100 times I get 100 times a text saying
Loading ...
Starting execution ...
Results
Passed/Failed 1
--------
Total 1
And 100 .trx files will be created...
What I actually want is having an output like this
Passed 97
Failed 3
--------
Total 100
and just one .trx file.
I somehow doubt that it's possible to run a test several times in a single test run at all since I didn't find any parameter on mstest /help output.
Is there a way to create an output as close as possible to the given request?
setlocal enabledelayedexpansion
set passed=0
set failed=0
for /l %%x in (1,1,%max%) do (
mstest /testcontainer:"path/test.dll" /test:"mytest"
if !errorlevel!==0 (
set /a passed=passed+1
) else (
set /a failed=failed+1
)
)
echo Passed %passed% > %~n1.trx
echo Failed %failed% >> %~n1.trx
echo --------- >> %~n1.trx
echo Total %Max% >> %~n1.trx
endlocal