Using CMD to find and count occurences? - windows

Simplification :
A long command yields counts of occurences :
echo 2 | find /C "2" //output "1"
This is because "2" appears only once (per line) hence the output 1.
But now I want to echo success only if the value is greater than 1. ( else throw error)
something like this :
echo 2 | find /C "2" | check if val >1 && echo "success" ELSE throw
Question:
I've managed to do the left part. But How can I create the right section ?

use conditional executing.
If you only want to check, if a string occures at least once, you don't need /c:
echo 2|find "2" >nul && echo success || echo fail
For other numbers (e.g "more than one"), you need to count /c and to capture the output with a for /f loop:
#echo off
setlocal
for /f %%a in ('type "%~f0"^|find /c "e"') do set count=%%a
echo %count%
if %count% gtr 1 (
echo more than one
) else (
echo one or less
)
For demo, I search the batchfile itself and count the lines containing (at least one) e. A for /f loop is used to capture the output of a command into a variable, which you then can compare with another value.
Just for academic reasons, without a for loop:
type test.txt|find /c "2"|findstr /xv "0 1" >nul && echo more than one
This works in your special case, but the for loop is more generic.
(I know, you are aware of it, but to make clear for future readers: find /c counts lines that contain the search string (at least once), not overall occurrences)

Related

Verify that user input is one of several allowed words

So I am having trouble with this line of code, which is meant to check if user input matches the given options.
set /p "myVar=---> "
echo %myVar%|findstr /ix "red:Red:blue:Blue">nul && (
echo %myVar% matched
) || (
echo %myVar% not matched
)
Is there a way I can go it like the above or any other way?
You could make use of the fact that for /F returns an exit code of 1 in case of zero iterations:
set /P VAR="Enter something: " || ((echo Empty input!) & exit /B)
(for /F "delims=AaEeIiOoUuYy eol=y" %%K in ("%VAR%") do rem/) && (
echo No match found!
) || (
echo Match encountered.
)
If the input consists only of characters listed after delims=, the for /F loop does not iterate and returns an exit code of 1; the command behind && only executes in case the exit code is 0, the command behind || only executes in case the exit code is not 0.
If no input is provided, set /P sets the exit code to 1.
Here is a basic example of what you want to do. I have not tested in DOS since I do not have any VM (or very old computer) right here, but IIRC this should work in plain DOS as well.
#echo off
if %var% == A GOTO :anything
if %var% == E GOTO :anything
if %var% == I GOTO :anything
if %var% == O GOTO :anything
if %var% == U GOTO :anything
if %var% == Y GOTO :anything
goto end
:anything
echo anything
:end
I think, choice is your best friend here. But if you want to do it without choice (it isn't available in all Windows versions):
set /p "myVar=---> "
echo %myVar%|findstr /ix "a e i o u y">nul && (
echo %myVar% matched
) || (
echo %myVar% not matched
)
It asks for input and checks if it is exactly one of the characters from the list, ignoring capitalization.

ERRORLEVEL in FOR /F Command Loop Returns Unexpected Result

I am trying to log the output of net stop while also capturing its ERRORLEVEL.
Based on this question, I attempted the following from within a nested subroutine:
set /a loopIndex=0
for /F "usebackq delims=" %%i in (`net stop %SERVICE_NAME%`) do (
if !loopIndex! EQU 0 if !errorlevel! EQU 1 set statementError=1
set /a loopIndex+=1
call :logMessage "%%i"
)
echo statementError: %statementError%
However, this does not work, throwing 1 even when net stop succeeds.
Is this possible without a temp file? If not, what would a temp file solution look like?
As #drruruu asked in this question :
Is this possible without a temp file?
Yes, it's possible without a temp file. By sending ERRORLEVEL to STDOUT in the IN clause and parse it in the LOOP clause. And it could be done with delayed expansion too.
For convenience, here is an example. It's somewhat a FINDSTR wrapper that search for a string in the batch itself. It covers all the common cases where you need to know what was going wrong, where and why :
Error in the DO () clause (aka the loop) and get the corresponding exit code
Error in the IN () clause and get the corresponding exit code
Error directly at the FOR clause (wrong syntax, bad delimiters, etc.)
The following script simulates theses situations with FINDSTR and flags as parameters :
The first parameter is the string to search.
The second parameter is a 0/1 flag to simulate an error not related to FINDSTR in the loop.
The third parameter is a way to simulate an error on the FOR clause itself (not on IN nor LOOP)
The fourth parameter is a way to test a FINDSTR which exit 255 when the file to search does not exist. Sadly, FINDSTR exit with 1 when it can't find a string in the file/files, but also exit with 1 when it can't find any files.. With the fourth parameter, we simulate a situation where FINDSTR exit with 255 when it can't find the file.
#echo off
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 (
ECHO Can't use extensions
EXIT /B 1
)
SETLOCAL ENABLEDELAYEDEXPANSION
IF ERRORLEVEL 1 (
ECHO Can't use delayed expansion
EXIT /B 1
)
REM The string to search
SET "LOCALV_STRING=%1"
REM The file to search. Myself.
SET "LOCALV_THIS=%0"
REM Store the exit code for the LOOP
SET "LOCALV_ERR="
REM Store the exit code for the IN
SET "LOCALV_RET="
REM Flag to stop parsing output for error simulation
SET "LOCALV_END="
REM To get the exit code of the IN clause, we get it through expansion with a second FOR loop using the well known CALL expansion and send it on STDOUT in the form "__<code>"
FOR /F "%~3tokens=*" %%M IN ('FINDSTR "!LOCALV_STRING!" "!LOCALV_THIS%~4!" ^
^& FOR /F %%A IN ^("ERRORLEVEL"^) DO #CALL ECHO __%%%%A%%') DO (
SET "LOCALV_TMP=%%~M"
REM Simulate that something goes wrong with FINDSTR I/O
IF NOT EXIST "!LOCALV_THIS!%~4" (
SET "LOCALV_RET=255"
SET LOCALV_END=1
)
IF "!LOCALV_END!" == "" (
REM SImulate a problem in the loop
IF "%2" == "1" (
(CMD /C EXIT /B 127)
SET LOCALV_END=1
) ELSE (
IF NOT "!LOCALV_TMP:~0,2!" == "__" ECHO Found: !LOCALV_TMP!
)
)
IF "!LOCALV_TMP:~0,2!!LOCALV_RET!" == "__" SET "LOCALV_RET=!LOCALV_TMP:__=!"
)
SET "LOCALV_ERR=!ERRORLEVEL!"
REM LOCALV_ERR get the exit code from the last iteration of the for loop
REM LOCALV_RET get the exit code from the IN command of the for loop
REM Sadly, FINDSTR exit with 1 if it did not find the string, but also with 1 if it could not found the file. To simulate a proper handling of exit code for
REM abnormal hardware/software situation, %2 is used to force a 255 exit code
REM If LOCALV_RET is not defined, this means the FOR...ECHO__.. wasn't executed, therefore there is a pb with the FOR LOOP
IF "!LOCALV_RET!" == "" (
ECHO Something went wrong with FOR...
EXIT /B 1
)
REM If LOCALV_RET is defined, this means the FOR...ECHO__.. was executed and the last loop operation has parsed the FINDSTR exit code, LOCALV_RET get its exit code
REM If LOCALV_RET is defined but LOCALV_ERR is not "0", something went wrong in the loop (I/O error, out of memory, wathever you could think), the problem is not FINDSTR
IF NOT "!LOCALV_ERR!" == "0" (
ECHO Error in the loop while searching "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
EXIT /B 4
)
REM If LOCALV_RET is "0", FINDSTR got matching strings in the file, if "1", FINDSTR don't find any matching string, if anything else, FINDSTR got a problem like failed I/O.
REM If LOCALV_RET is "0" and LOCALV_ERR is "0", everything is ok.
IF "!LOCALV_RET!" == "0" (
ECHO Success.
EXIT /B 0
)
REM If LOCALV_RET is "1" and LOCALV_ERR is "0", FINDSTR failed to find the string in the file "or" failed to find file, for the latter we simulate that FINDSTR exit with 255 .
IF "!LOCALV_RET!" == "1" (
ECHO FINDSTR failed to find "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
EXIT /B 2
)
REM If LOCALV_RET isn't "0" nor "1" and LOCALV_ERR is "0", FINDSTR failed to do the job and LOCALV_RET got the exit code.
ECHO FINDSTR: Houst^W OP, we've got a problem here while searching "!LOCALV_STRING!" in "!LOCALV_THIS!", exit code !LOCALV_RET!. Loop exit code : !LOCALV_ERR!.
EXIT /B 3
Script output :
Normal operation, no error simulation.
PROMPT>.\for.bat FOR 0 "" ""
Found: FOR /F "%~3tokens=*" %%M IN ('FINDSTR "FOR" ""
Found: ^& FOR /F %%A IN ^("ERRORLEVEL"^) DO #CALL ECHO __%%%%A%%') DO (
Found: REM If LOCALV_RET is not defined, this means the FOR...ECHO__.. wasn't executed, therefore there is a pb with the FOR LOOP
Found: ECHO Something went wrong with FOR...
Found: REM If LOCALV_RET is defined, this means the FOR...ECHO__.. was executed and the last loop operation has parsed the FINDSTR exit code, LOCALV_RET get its exit code
Success.
Normal operation, no error simulation, with a string that FINDSTR can't find in the file.
PROMPT>.\for.bat ZZZ 0 "" ""
FINDSTR failed to find "ZZZ" in ".\for.bat", exit code 1. Loop exit code : 0.
Simulate an error in the LOOP clause, not related to FINDSTR.
PROMPT>.\for.bat FOR 1 "" ""
Error in the loop while searching "FOR" in ".\for.bat", exit code 0. Loop exit code : 127.
Simulate an error in the FOR clause at start with an unknow "delimstoken" option.
PROMPT>.\for.bat FOR 0 "delims" ""
delimstokens=*" was unexpected.
Something went wrong with FOR...
Simulate FINDSTR exiting 255 if it can't find the file.
PROMPT>.\for.bat FOR 1 "" "ERR"
FINDSTRĀ : Can't open
FINDSTR: HoustW OP, we've got a problem here while searching "FOR" in ".\for.bat", exit code 255. Loop exit code : 0.
The FOR /F command executes NET STOP in a new cmd.exe process. FOR /F processes stdout, but that is it. There is no way for the main script to see any variable values that the FOR /F command might create, as they are gone once the sub-process terminates.
The simplest and most efficient solutions use a temporary file. I'm assuming NET STOP has two possible error codes - Success = 0, and Error = 1. So the simplest solution is to simply create a temporary error signal file if there was error.
The following demonstrates the concept in a generic way:
#echo off
del error.flag 2>nul
for /f "delims=" %%A in ('net stop %SERVICE_NAME% ^|^| echo error>error.flag') do (
...
)
if exist error.flag (
echo There was an error
del error.flag
)
You could just as easily put the error test within the DO() code if desired.
While #dbenham's answer is suitable for cases where %ERRORLEVEL% returns a binary value, I was not able to confirm or deny if the returned exit codes for net stop are in fact binary and so opted for an n-ary solution.
As per #dbenham's
DOS tips forum post:
FOR /F "delims=" %%i IN ('net stop MyService 2^>^&1 ^& CALL ECHO %%^^ERRORLEVEL%%^>error.level') DO (
CALL :logMessage "%%i"
)
FOR /F "delims=" %%i IN (error.level) DO (SET /A statementError=%%i)
DEL error.level
IF %statementError% NEQ 0 ()
Breaking down the statement parsing:
net stop MyService 2^>^&1 ^& CALL ECHO %%^^ERRORLEVEL%%^>error.level
net stop MyService 2>&1 & CALL ECHO %^ERRORLEVEL%>error.level
echo %ERRORLEVEL%>error.level
Here, CALL is used specifically to delay parsing of %ERRORLEVEL% until execution of ECHO.

If else function in .bat script + findstr command

IF find tcp.client == 1
(
findstr tcp.client *_chkpackage.log>summary.txt
)
ELSE
(
#write only "filename" & "N/A" >> summary.txt
)
i want to search a file that search some value in all text files in folder
if found the line that contain information i need it is going to write that line to text fiel
if not found the it will write only filename and "N/A" to the line
i know it wrong but my coding skill suck so i have to ask
thank you so much for the answer
Something that works according to your "specification" would be that one:
#echo off
if exist summary.txt del summary.txt
for %%f in (*_chkpackage.log) do (
find "tcp.client" %%f>NUL:
IF errorlevel 1 (
echo %%f N/A >> summary.txt
) ELSE (
findstr "tcp.client" %%f >> summary.txt
)
)
type summary.txt
Note that the if errorlevel 1 means "if errorlevel is greater or equal 1", for that I swapped your comparison, because if errorlevel 0 is true even when errorlevel equals 1.
I'm assuming the dot is a dot and not a regex wildcard, in which case you need to tell FINDSTR to do a literal search.
FINDSTR returns success if found, error if not found. The || operator conditionally executes commands if the prior command failed.
Use FOR loop to get each file individually, and add an additional file (nul) that will never match to force inclusion of file name in output.
Enclose entire construct in parentheses and redirect the entire block
#echo off
>summary.txt (
for %%F in (*_chkpackage.log) do findstr /l "tcp.client" "%%F" nul||echo %%F N/A
)

IF... OR IF... in a windows batch file

Is there a way to write an IF OR IF conditional statement in a windows batch-file?
For example:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
The zmbq solution is good, but cannot be used in all situations, such as inside a block of code like a FOR DO(...) loop.
An alternative is to use an indicator variable. Initialize it to be undefined, and then define it only if any one of the OR conditions is true. Then use IF DEFINED as a final test - no need to use delayed expansion.
FOR ..... DO (
set "TRUE="
IF cond1 set TRUE=1
IF cond2 set TRUE=1
IF defined TRUE (
...
) else (
...
)
)
You could add the ELSE IF logic that arasmussen uses on the grounds that it might perform a wee bit faster if the 1st condition is true, but I never bother.
Addendum - This is a duplicate question with nearly identical answers to Using an OR in an IF statement WinXP Batch Script
Final addendum - I almost forgot my favorite technique to test if a variable is any one of a list of case insensitive values. Initialize a test variable containing a delimitted list of acceptable values, and then use search and replace to test if your variable is within the list. This is very fast and uses minimal code for an arbitrarily long list. It does require delayed expansion (or else the CALL %%VAR%% trick). Also the test is CASE INSENSITIVE.
set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" (echo true) else (echo false)
The above can fail if VAR contains =, so the test is not fool-proof.
If doing the test within a block where delayed expansion is needed to access current value of VAR then
for ... do (
set "TEST=;val1;val2;val3;val4;val5;"
for /f %%A in (";!VAR!;") do if "!TEST:%%A=!" neq "!TEST!" (echo true) else (echo false)
)
FOR options like "delims=" might be needed depending on expected values within VAR
The above strategy can be made reliable even with = in VAR by adding a bit more code.
set "TEST=;val1;val2;val3;val4;val5;"
if "!TEST:;%VAR%;=!" neq "!TEST!" if "!TEST:;%VAR%;=;%VAR%;"=="!TEST!" echo true
But now we have lost the ability of providing an ELSE clause unless we add an indicator variable. The code has begun to look a bit "ugly", but I think it is the best performing reliable method for testing if VAR is any one of an arbitrary number of case-insensitive options.
Finally there is a simpler version that I think is slightly slower because it must perform one IF for each value. Aacini provided this solution in a comment to the accepted answer in the before mentioned link
for %%A in ("val1" "val2" "val3" "val4" "val5") do if "%VAR%"==%%A echo true
The list of values cannot include the * or ? characters, and the values and %VAR% should not contain quotes. Quotes lead to problems if the %VAR% also contains spaces or special characters like ^, & etc. One other limitation with this solution is it does not provide the option for an ELSE clause unless you add an indicator variable. Advantages are it can be case sensitive or insensitive depending on presence or absence of IF /I option.
I don't think so. Just use two IFs and GOTO the same label:
IF cond1 GOTO foundit
IF cond2 GOTO foundit
ECHO Didn't find it
GOTO end
:foundit
ECHO Found it!
:end
A simple "FOR" can be used in a single line to use an "or" condition:
FOR %%a in (item1 item2 ...) DO IF {condition_involving_%%a} {execute_command}
Applied to your case:
FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE
Suppress executing twice
A comment pointed out that {execute_command} may be encountered twice. To avoid this, you can use a goto after the first encounter.
FOR %%a in (1 2) DO IF %var%==%%a (
ECHO TRUE
goto :continue
)
:continue
If you think there's a possibility that {execute_command} might be executed twice and you don't want that, you can just add && goto :eof:
FOR %%a in (1 2) DO IF %var%==%%a ECHO TRUE && goto :eof
Much simpler, and still on a single line.
Thanks for this post, it helped me a lot.
Dunno if it can help but I had the issue and thanks to you I found what I think is another way to solve it based on this boolean equivalence:
"A or B" is the same as "not(not A and not B)"
Thus:
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
Becomes:
IF not [%var%] == [1] IF not [%var%] == [2] ECHO FALSE
Even if this question is a little older:
If you want to use if cond1 or cond 2 - you should not use complicated loops or stuff like that.
Simple provide both ifs after each other combined with goto - that's an implicit or.
//thats an implicit IF cond1 OR cond2 OR cond3
if cond1 GOTO doit
if cond2 GOTO doit
if cond3 GOTO doit
//thats our else.
GOTO end
:doit
echo "doing it"
:end
Without goto but an "inplace" action, you might execute the action 3 times, if ALL conditions are matching.
There is no IF <arg> OR or ELIF or ELSE IF in Batch, however...
Try nesting the other IF's inside the ELSE of the previous IF.
IF <arg> (
....
) ELSE (
IF <arg> (
......
) ELSE (
IF <arg> (
....
) ELSE (
)
)
The goal can be achieved by using IFs indirectly.
Below is an example of a complex expression that can be written quite concisely and logically in a CMD batch, without incoherent labels and GOTOs.
Code blocks between () brackets are handled by CMD as a (pathetic) kind of subshell. Whatever exit code comes out of a block will be used to determine the true/false value the block plays in a larger boolean expression. Arbitrarily large boolean expressions can be built with these code blocks.
Simple example
Each block is resolved to true (i.e. ERRORLEVEL = 0 after the last statement in the block has executed) / false, until the value of the whole expression has been determined or control jumps out (e.g. via GOTO):
((DIR c:\xsgdde /w) || (DIR c:\ /w)) && (ECHO -=BINGO=-)
Complex example
This solves the problem raised initially. Multiple statements are possible in each block but in the || || || expression it's preferable to be concise so that it's as readable as possible. ^ is an escape char in CMD batches and when placed at the end of a line it will escape the EOL and instruct CMD to continue reading the current batch of statements on the next line.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
(
(CALL :ProcedureType1 a b) ^
|| (CALL :ProcedureType2 sgd) ^
|| (CALL :ProcedureType1 c c)
) ^
&& (
ECHO -=BINGO=-
GOTO :EOF
)
ECHO -=no bingo for you=-
GOTO :EOF
:ProcedureType1
IF "%~1" == "%~2" (EXIT /B 0) ELSE (EXIT /B 1)
GOTO :EOF (this line is decorative as it's never reached)
:ProcedureType2
ECHO :ax:xa:xx:aa:|FINDSTR /I /L /C:":%~1:">nul
GOTO :EOF
It's possible to use a function, which evaluates the OR logic and returns a single value.
#echo off
set var1=3
set var2=5
call :logic_or orResult "'%var1%'=='4'" "'%var2%'=='5'"
if %orResult%==1 (
echo At least one expression is true
) ELSE echo All expressions are false
exit /b
:logic_or <resultVar> expression1 [[expr2] ... expr-n]
SETLOCAL
set "logic_or.result=0"
set "logic_or.resultVar=%~1"
:logic_or_loop
if "%~2"=="" goto :logic_or_end
if %~2 set "logic_or.result=1"
SHIFT
goto :logic_or_loop
:logic_or_end
(
ENDLOCAL
set "%logic_or.resultVar%=%logic_or.result%"
exit /b
)
If %x%==1 (
If %y%==1 (
:: both are equal to 1.
)
)
That's for checking if multiple variables equal value. Here's for either variable.
If %x%==1 (
:: true
)
If %x%==0 (
If %y%==1 (
:: true
)
)
If %x%==0 (
If %y%==0 (
:: False
)
)
I just thought of that off the top if my head. I could compact it more.
I realize this question is old, but I wanted to post an alternate solution in case anyone else (like myself) found this thread while having the same question. I was able to work around the lack of an OR operator by echoing the variable and using findstr to validate.
for /f %%v in ('echo %var% ^| findstr /x /c:"1" /c:"2"') do (
if %errorlevel% equ 0 echo true
)
While dbenham's answer is pretty good, relying on IF DEFINED can get you in loads of trouble if the variable you're checking isn't an environment variable. Script variables don't get this special treatment.
While this might seem like some ludicrous undocumented BS, doing a simple shell query of IF with IF /? reveals that,
The DEFINED conditional works just like EXIST except it takes an
environment variable name and returns true if the environment variable
is defined.
In regards to answering this question, is there a reason to not just use a simple flag after a series of evaluations? That seems the most flexible OR check to me, both in regards to underlying logic and readability. For example:
Set Evaluated_True=false
IF %condition_1%==true (Set Evaluated_True=true)
IF %some_string%=="desired result" (Set Evaluated_True=true)
IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)
IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)
Obviously, they can be any sort of conditional evaluation, but I'm just sharing a few examples.
If you wanted to have it all on one line, written-wise, you could just chain them together with && like:
Set Evaluated_True=false
IF %condition_1%==true (Set Evaluated_True=true) && IF %some_string%=="desired result" (Set Evaluated_True=true) && IF %set_numerical_variable% EQ %desired_numerical_value% (Set Evaluated_True=true)
IF %Evaluated_True%==true (echo This is where you do your passing logic) ELSE (echo This is where you do your failing logic)
Never got exist to work.
I use
if not exist g:xyz/what goto h:
Else xcopy c:current/files g:bu/current
There are modifiers /a etc. Not sure which ones. Laptop in shop. And computer in office. I am not there.
Never got batch files to work above Windows XP
A much faster alternative I usually use is as follows, as I can "or" an arbitrary number of conditions that can fit in variable space
#(
Echo off
Set "_Match= 1 2 3 "
)
Set /a "var=3"
Echo:%_Match%|Find " %var% ">nul || (
REM Code for a false condition goes here
) && (
REM code for a true condition goes here.
)
it's quite simple, just use below
IF %var% == 1 (
ECHO TRUE)
IF %var% == 2 (
ECHO TRUE)
Another option is to display the current environment variables and exploit the default behaviour of FINDSTR:
FINDSTR "hello there" x.y searches for "hello" or "there" in file x.y.
So
SET | FINDSTR /I /X "var=1 var=2" >NUL
IF %ERRORLEVEL% EQU 0 (
ECHO TRUE
) ELSE (
ECHO FALSE
)
Where
/I Specifies that the search is not to be case-sensitive.
/X Prints lines that match exactly.
If regular expressions are preferred, use FINDSTR /R /I "^var=1$ ^var=2$" >NUL instead.
Edit: FINDSTR /R should be used if the variable string includes a space, e.g., FINDSTR /R /I "^var=1 a$ ^var=2 b$" >NUL.
Edit: If the variable string includes spaces, a literal search string should be used. E.g., FINDSTR /I /X /C:"var=1 a" /C:"var=2 b" >NUL.
There is no OR operator but you can write (the pseudocode)
IF [%var%] == [1] OR IF [%var%] == [2] ECHO TRUE
like
IF "%var%" == "1" SET "match=y"
IF "%var%" == "2" SET "match=y"
IF DEFINED match ECHO TRUE
Note that the double quotes prevents a syntax error from being triggered if var is undefined.
I took bogdan's solution to the next level by building an extern function that is a callable and clean abstraction of IF, so it can be used in inline blocks. Don't look further, if you build a batch library anyways.
lib.cmd
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS
SHIFT & GOTO:%1
: Common batch extension library.
:::
: Performs conditional processing in batch programs. Is callable for inline use.
: Arguments:
: %1 - /I for case-insensitive comparison on strings, can be skipped for case-sensitive comparison.
: %2 - NOT to negate the result, can be skipped.
: %3 - EXIST for file checks or first string to compare with, also supports "string1"=="string2" (full condition).
: %4 - If EXIST is specified, path to the directory or file.
: Otherwise if %3 is not a full condition, this argument has to be one of:
: - == Compares both strings to be equal using lstrcmpW or lstrcmpiW (case-insensitive).
: - EQU Converts both strings to numbers and checks if they are equal.
: - NEQ Converts both strings to numbers and checks if they are not equal.
: - LSS Converts both strings to numbers and checks if the first is lesser than the second.
: - LEQ Converts both strings to numbers and checks if the first is lesser or equal than the second.
: - GTR Converts both strings to numbers and checks if the first is greater than the second.
: - GEQ Converts both strings to numbers and checks if the first is greater or equal than the second.
: If a string cannot be parsed to a number, its numeric representation will be used.
: This argument can be skipped, so == will be used.
: %5 - If %3 is not a full condition, this argument has to be the second string to compare.
: Outputs:
: Nothing
: Returns:
: 0, if the condition is met, 1 otherwise
:::
:test-if
IF "%~1"=="/I" SET "I=/I " & SHIFT
IF "%~1"=="/i" SET "I=/I " & SHIFT
IF "%~1"=="NOT" SET "NOT=NOT " & SHIFT
IF "%~1"=="not" SET "NOT=NOT " & SHIFT
IF "%~1"=="EXIST" SET "EXIST=EXIST " & SHIFT
IF "%~1"=="exist" SET "EXIST=EXIST " & SHIFT
SET "string1=%~1%"
IF "%~3"=="" (
SET "comp=^=^="
SET "string2=%~2"
) ELSE (
SET "comp=%~2"
SET "string2=%~3"
)
IF %I%%NOT%%EXIST% "%string1%" %comp% "%string2%" (
ENDLOCAL & EXIT /B 0
)
ENDLOCAL & EXIT /B 1
Usage
( ( CALL lib test-if "%1" == "foo" ) || ( CALL lib test-if "%1" == "bar" ) ) && (
ECHO "Argument is foo or bar"
)
lib is the path to the lib.cmd, the suffix .cmd is not mandatory for cmd-files
Any IF syntax is compatible with this abstraction, so you can also do things like test-if EXIST "path" or test-if not 300 LSS 200
The == in test-if "%1" == "foo" will be stripped away by batch and I address this fact in my case, but this causes test-if "%1" "foo" to be valid as well, it's not the standard though.
Realizing this is a bit of an old question, the responses helped me come up with a solution to testing command line arguments to a batch file; so I wanted to post my solution as well in case anyone else was looking for a similar solution.
First thing that I should point out is that I was having trouble getting IF ... ELSE statements to work inside of a FOR ... DO clause. Turns out (thanks to dbenham for inadvertently pointing this out in his examples) the ELSE statement cannot be on a separate line from the closing parens.
So instead of this:
FOR ... DO (
IF ... (
)
ELSE (
)
)
Which is my preference for readability and aesthetic reasons, you have to do this:
FOR ... DO (
IF ... (
) ELSE (
)
)
Now the ELSE statement doesn't return as an unrecognized command.
Finally, here's what I was attempting to do - I wanted to be able to pass several arguments to a batch file in any order, ignoring case, and reporting/failing on undefined arguments passed in. So here's my solution...
#ECHO OFF
SET ARG1=FALSE
SET ARG2=FALSE
SET ARG3=FALSE
SET ARG4=FALSE
SET ARGS=(arg1 Arg1 ARG1 arg2 Arg2 ARG2 arg3 Arg3 ARG3)
SET ARG=
FOR %%A IN (%*) DO (
SET TRUE=
FOR %%B in %ARGS% DO (
IF [%%A] == [%%B] SET TRUE=1
)
IF DEFINED TRUE (
SET %%A=TRUE
) ELSE (
SET ARG=%%A
GOTO UNDEFINED
)
)
ECHO %ARG1%
ECHO %ARG2%
ECHO %ARG3%
ECHO %ARG4%
GOTO END
:UNDEFINED
ECHO "%ARG%" is not an acceptable argument.
GOTO END
:END
Note, this will only report on the first failed argument. So if the user passes in more than one unacceptable argument, they will only be told about the first until it's corrected, then the second, etc.

Batch file: Find if substring is in string (not in a file)

In a batch file, I have a string abcdefg. I want to check if bcd is in the string.
Unfortunately it seems all of the solutions I'm finding search a file for a substring, not a string for a substring.
Is there an easy solution for this?
Yes, you can use substitutions and check against the original string:
if not x%str1:bcd=%==x%str1% echo It contains bcd
The %str1:bcd=% bit will replace a bcd in str1 with an empty string, making it different from the original.
If the original didn't contain a bcd string in it, the modified version will be identical.
Testing with the following script will show it in action:
#setlocal enableextensions enabledelayedexpansion
#echo off
set str1=%1
if not x%str1:bcd=%==x%str1% echo It contains bcd
endlocal
And the results of various runs:
c:\testarea> testprog hello
c:\testarea> testprog abcdef
It contains bcd
c:\testarea> testprog bcd
It contains bcd
A couple of notes:
The if statement is the meat of this solution, everything else is support stuff.
The x before the two sides of the equality is to ensure that the string bcd works okay. It also protects against certain "improper" starting characters.
You can pipe the source string to findstr and check the value of ERRORLEVEL to see if the pattern string was found. A value of zero indicates success and the pattern was found. Here is an example:
::
: Y.CMD - Test if pattern in string
: P1 - the pattern
: P2 - the string to check
::
#echo off
echo.%2 | findstr /C:"%1" 1>nul
if errorlevel 1 (
echo. got one - pattern not found
) ELSE (
echo. got zero - found pattern
)
When this is run in CMD.EXE, we get:
C:\DemoDev>y pqrs "abc def pqr 123"
got one - pattern not found
C:\DemoDev>y pqr "abc def pqr 123"
got zero - found pattern
I usually do something like this:
Echo.%1 | findstr /C:"%2">nul && (
REM TRUE
) || (
REM FALSE
)
Example:
Echo.Hello world | findstr /C:"world">nul && (
Echo.TRUE
) || (
Echo.FALSE
)
Echo.Hello world | findstr /C:"World">nul && (Echo.TRUE) || (Echo.FALSE)
Output:
TRUE
FALSE
I don't know if this is the best way.
For compatibility and ease of use it's often better to use FIND to do this.
You must also consider if you would like to match case sensitively or case insensitively.
The method with 78 points (I believe I was referring to paxdiablo's post) will only match Case Sensitively, so you must put a separate check for every case variation for every possible iteration you may want to match.
( What a pain! At only 3 letters that means 9 different tests in order to accomplish the check! )
In addition, many times it is preferable to match command output, a variable in a loop, or the value of a pointer variable in your batch/CMD which is not as straight forward.
For these reasons this is a preferable alternative methodology:
Use: Find [/I] [/V] "Characters to Match"
[/I] (case Insensitive)
[/V] (Must NOT contain the characters)
As Single Line:
ECHO.%Variable% | FIND /I "ABC">Nul && ( Echo.Found "ABC" ) || ( Echo.Did not find "ABC" )
Multi-line:
ECHO.%Variable%| FIND /I "ABC">Nul && (
Echo.Found "ABC"
) || (
Echo.Did not find "ABC"
)
As mentioned this is great for things which are not in variables which allow string substitution as well:
FOR %A IN (
"Some long string with Spaces does not contain the expected string"
oihu AljB
lojkAbCk
Something_Else
"Going to evaluate this entire string for ABC as well!"
) DO (
ECHO.%~A| FIND /I "ABC">Nul && (
Echo.Found "ABC" in "%A"
) || ( Echo.Did not find "ABC" )
)
Output From a command:
NLTest | FIND /I "ABC">Nul && ( Echo.Found "ABC" ) || ( Echo.Did not find "ABC" )
As you can see this is the superior way to handle the check for multiple reasons.
If you are detecting for presence, here's the easiest solution:
SET STRING=F00BAH
SET SUBSTRING=F00
ECHO %STRING% | FINDSTR /C:"%SUBSTRING%" >nul & IF ERRORLEVEL 1 (ECHO CASE TRUE) else (ECHO CASE FALSE)
This works great for dropping the output of windows commands into a boolean variable. Just replace the echo with the command you want to run. You can also string Findstr's together to further qualify a statement using pipes. E.G. for Service Control (SC.exe)
SC QUERY WUAUSERV | findstr /C:"STATE" | FINDSTR /C:"RUNNING" & IF ERRORLEVEL 1 (ECHO case True) else (ECHO CASE FALSE)
That one evaluates the output of SC Query for windows update services which comes out as a multiline text, finds the line containing "state" then finds if the word "running" occurs on that line, and sets the errorlevel accordingly.
I'm probably coming a bit too late with this answer, but the accepted answer only works for checking whether a "hard-coded string" is a part of the search string.
For dynamic search, you would have to do this:
SET searchString=abcd1234
SET key=cd123
CALL SET keyRemoved=%%searchString:%key%=%%
IF NOT "x%keyRemoved%"=="x%searchString%" (
ECHO Contains.
)
Note: You can take the two variables as arguments.
To find a text in the Var, Example:
var_text="demo string test"
Echo.%var_text% | findstr /C:"test">nul && (
echo "found test"
) || Echo.%var_text% | findstr /C:"String">nul && (
echo "found String with S uppercase letter"
) || (
echo "Not Found "
)
LEGEND:
& Execute_that AND execute_this
|| Ex: Execute_that IF_FAIL execute this
&& Ex: Execute_that IF_SUCCESSFUL execute this
>nul no echo result of command
findstr
/C: Use string as a literal search string
Best approach for me would be this:
call :findInString "HelloWorld" "World"
:findInString
ECHO.%1 | FIND /I %2>Nul && (
Echo.Found
) || (
Echo.Not found
)
ECHO %String%| FINDSTR /C:"%Substring%" && (Instructions)
Better answer was here:
set "i=hello " world"
set i|find """" >nul && echo contains || echo not_contains
The solutions that search a file for a substring can also search a string, eg. find or findstr.
In your case, the easy solution would be to pipe a string into the command instead of supplying a filename eg.
case-sensitive string:
echo "abcdefg" | find "bcd"
ignore case of string:
echo "abcdefg" | find /I "bcd"
IF no match found, you will get a blank line response on CMD and %ERRORLEVEL% set to 1
Built on #user839791's answer, but I've added a few more things.
#echo off
rem --Set variable below--
set var=condition
rem --Uncomment below line to display contents of variable--
::echo The variable is %var%
rem --Change condition to desired string below--
ECHO.%var%| FIND /I "condition">Nul && (
rem --Occurs if the string is found--
Echo.Variable is "condition"
color C
pause
) || (
rem --Occurs if the string isn't found--
Echo.Variable is not "condition"
color A
pause
)
Yes, We can find the subString in the String:
echo.%data% | FINDSTR /I "POS">Nul && (SET var=POS) || (SET noVar="variable not found")
echo.%data% | FINDSTR /I "TD1">Nul && (SET var=TD1) || (SET noVar="variable not found")
GOTO %var%
:POS
echo processes inside POS
GOTO END
:TD1
echo processes inside TD1
:END

Resources