1

When calling a

find /V /I

only with /V(!) the ERRORLEVEL is always 0 it appears to me. (Using Win7 x64)

If, for example, I run the following batch file

@echo off
SETLOCAL enabledelayedexpansion
find /V /I "camstart" testtest.txt
echo Errorlevel is !ERRORLEVEL!

on this testtest.txt file

intrinsicParamStatus(2338763)
calibrationFormatID(260)
calibrationFormatID(260)
leftCamStartX(88)
leftCamStartY(170)
rightCamStartX(88)

the output is:

---------- TESTTEST.TXT
intrinsicParamStatus(2338763)
calibrationFormatID(260)
calibrationFormatID(260)
Errorlevel is 0

and if I run

@echo off
SETLOCAL enabledelayedexpansion
find /V /I "camstarRt" testtest.txt
echo Errorlevel is !ERRORLEVEL!

the output is

intrinsicParamStatus(2338763)
calibrationFormatID(260)
calibrationFormatID(260)
leftCamStartX(88)
leftCamStartY(170)
rightCamStartX(88)
Errorlevel is 0

Evidently the output matches the task to find the string "camStarRt", which is not there, so it outputs all lines. But why doesn't that change the errorlevel?

When do exactly the same with

@echo off
SETLOCAL enabledelayedexpansion
find /I "camstarrt" testtest.txt
echo Errorlevel is !ERRORLEVEL!

The errorlevel becomes 1, as expected.

Is this a bug in find? How can I come across this?

My goal is to execute a task if a certain string in the output of a python-script is found. But if it's not, all lines of it shall be displayed as usual to see thework of the script.

python.exe C:\pycan-bin\xxx\yyy.py -m read -p lala16 -o %outfile% parameters.json | find /V /I "Response timeout"
commander_keen
  • 691
  • 1
  • 6
  • 11

2 Answers2

2

find will filter the lines that match a requested condition.

As some lines have passed the filter there is no reason to raise the errorlevel to signal that no matching lines have been found so it will be set to 0

errorlevel will be raised (set to 1) only when find does not echo anything, that is, there is no any line matching the request.

You can try with

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "timeoutFound="
    for /f "delims=" %%a in ('
        python.exe C:\pycan-bin\xxx\yyy.py -m read -p lala16 -o %outfile% parameters.json 
    ') do (
        set "line=%%a"
        setlocal enabledelayedexpansion
        if not "!line:Response timeout=!"=="!line!" set "timeoutFound=1"
        echo(!line!
        for %%b in ("!timeoutFound!") do endlocal & set "timeoutFound=%%~b"
    )

    if defined timeoutFound (
        rem Do Something
    )

The basic idea is to execute the command inside a for /f command. It will iterate over the ouput lines of the invoked python. In each iteration the for replaceable parameter will hold the line being processed.

This value is assigned to a variable to be able to use string substitution, replacing "Response timeout" with nothing. If the variable with the substring removed is equal to the variable, the substring is not contained, else the substring is present and a variable (timeoutFound) is defined to indicate it. Later the variable definition will be used to determine if the task needs to be execute.

When a block of commands is parsed before being executed (the case of the for command), all variable read operations are removed, replaced with the value in the variables before block execution. But we need to assign a value to a variable inside the for loop to do the substring operation. To deal with it, delayed expansion is needed (more here).

Community
  • 1
  • 1
MC ND
  • 69,615
  • 8
  • 84
  • 126
  • That means that http://ss64.com/nt/find.html is not very clear about the errorlevel then? As it states that errorlevel "1 String not found", not that there shall be no output. Well my goal remains the same though. ;-) – commander_keen May 22 '15 at 14:09
  • @commander_keen, no, ss64 is right. `errorlevel` is set if `find` does not found any string matching the request. You request the lines that does not include some string; as no line includes the string, lines are found that match the request so `errorlevel` is not set. Also, answer updated. – MC ND May 22 '15 at 14:17
  • Thanks for the suggestion. Unfortunately the part _if not "!line:Response timeout=!"=="!line!"_ is beyond my batch knowledge. Could you please explain, what this syntax with the colon : and the equal sign = in the end does? (or refer to a source where I can read about that? I seem to be unable to find a reference for this. I guess, I might figure it out with a documentation) – commander_keen May 22 '15 at 14:29
  • @commander_keen reference to colon batch variable feature [http://www.dostips.com/DtTipsStringManipulation.php#Snippets.Replace](http://www.dostips.com/DtTipsStringManipulation.php#Snippets.Replace). It is the string replace functionality. – David Ruhmann May 22 '15 at 14:44
  • @MCND: The `find.exe` external command set the `errorlevel` _always_. – Aacini May 22 '15 at 15:08
  • @Aacini, I used the term `set` in the sense of *being set to any value greater than 0*. Changed the term to `raise` in the answer. Is this a better term? – MC ND May 22 '15 at 15:32
  • I read your description and immediately remember `set /P` command, that set errorlevel to 1 if the line read is empty; otherwise _it does NOT set the errorlevel_, so it keep the same value it had before the `set /P`. `find` command certainly does not the same... – Aacini May 22 '15 at 16:06
  • @MCND: Thank you for adding an explanation. I've also read about the replacement ioperator in the meantime. But: I think _timeoutFound_ is not available after "endlocal" in the code above. – commander_keen Jun 08 '15 at 13:15
  • @MC ND: changing the line "endlocal" to "endlocal & set timeoutFound=!timeoutFound!" works around this. I hope this is "clean", in that case you might want to add it to your Code above. :-) Thank you. – commander_keen Jun 08 '15 at 13:38
  • @commander_keen, I was blind. It was a big mistake. Now corrected, but `endlocal & set "timeoutFound=!timeoutFound!"` will not work because the delayed expansion is disabled (by the `endlocal`) before the `set` command is executed, so `!timeoutFound!` is parsed as a string, not a variable reference. – MC ND Jun 08 '15 at 14:19
  • @MC ND: It appeared to me, that the delayed expansion is not disabled before the line with _endlocal_ was completely parsed. I was a bit curios/surprised, but in fact it worked fine with the & operator. – commander_keen Jun 09 '15 at 08:31
  • @commander_keen, it works fine IF the timeout string is found. But if it is not found, it will fail as the variable is assigned the `!timeoutFound!` string. As it is using delayed expansion, the value is not expanded at parse time, but at command execution. So, after the `endlocal` is executed delayed expansion is not active and to the parser `!timeoutFound!` is not a reference to a variable, it is a string. – MC ND Jun 09 '15 at 08:41
  • @MC ND: Got it, thanks. One issue is left though: When there is no timeout right in the first run, then the output-lines of the python script will be held back until everything is finished (which is like 20 seconds for me with just a blinking cursor). Then all output is pushed through in milliseconds. I don't find the reason, why all lines are displayed directly when it first runs into timeout and I call the script again compared to when it doesn't timeout and runs through directly. – commander_keen Jun 09 '15 at 09:54
  • @commander_keen, the `for /f` has been coded (the code in `cmd.exe`) to first load into memory all the data it will handle, being it a file to read or a command to execute. In your case, it will not start to process the output of the `python` command until it has finished. – MC ND Jun 09 '15 at 10:06
  • Ugh, that makes it a bit weird staring at a cursor where output is expected. But I guess this can't be avoided then(?) :-S Is there a limit for that what _for_ can handle? Because right now the python-script will output at least 600 lines (approx. 30-40KB of text) which might become more. – commander_keen Jun 09 '15 at 10:31
2

Although the find /? help does not describe this point specifically, the standard for errorlevel values is "0 if the command had success, 1 or more if it fails", and in the case of find, it returns 1 only when it does NOT find/display a line.

In your first two examples: find /V /I "camstart" testtest.txt and find /V /I "camstarRt" testtest.txt the result should be the same, because the /I switch indicate to treat "camstart" and "camstaRt" and "camStart" and "CamStart" strings the same.

The code below "execute a task if "camstart" string in the output is found (ignoring case). But if it's not, all lines of it shall be displayed as usual to see the work of the script."

find /I "camstart" testtest.txt > linesFound.txt
if !errorlevel! equ 0 (
    type linesFound.txt
    execute the task here
) else (
    type testtest.txt
)
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • As you copied my writing, camstarRt was a word with an _additional_ R on purpose, to make it not find the string. ;-) I won't care about the case. Thanks for your additional input. It uses an additional file but is easier to understand when I adapt it to my problem (where the output of the python-command is being parsed, not a text-file. – commander_keen Jun 08 '15 at 12:38
  • Oops! I see this point _now_, my mistake... **`:(`** However, if the purpose of the topic is to _clearly_ describe the problem, then you should use a clearer example, like `camStarXt`, or `camStarABCt`, etc. Why do you use precisely an example that can go unnoticed? – Aacini Jun 08 '15 at 15:49
  • My bad, I will keep it in mind to make it better in the future. – commander_keen Jun 09 '15 at 08:32