Strange command shell behavior... maybe a bug?

M

Matt Young

Greetings,

Today, I was building a script and came across something I couldn't explain.
If anyone can explain the behavior I'm experiencing I would appreciate it!

Here is the scenario, for each txt file in a directory, I'm using looking
for the string 'error' in it. Look at the code below.

I'm using a FOR to loop through the txt files and the FIND command to search
the files for 'error'.

Here is where I'm at a loss. I expect FIND will return an errorlevel of 0 if
it finds the string 'error' and errorlevel 1 if not for each file. But, in
reality, the errorlevel isn't changing until the FOR loop is exited.

Can anyone explain this behavior?

I found if I replace,
find /I "error" test.txt
with,
find /I "error" test.txt || echo failed

I can test if each file has passed or failed...

Thanks in advance! :)
Matt Young

-----------------------
@echo off
REM: Test batch one
echo %ERRORLEVEL% at start
REM: create a text file for test
echo asdf>test.txt
for /f %%i in ('dir /b *.txt') do (
echo testing %%i
REM: find should change errorlevel to 1
find /I "error" %%i
echo %ERRORLEVEL% inside for loop
)
echo %ERRORLEVEL% outside for loop
REM: delete the testing text file
del test.txt

-----------------------
output from running the batch on WinXP Pro and Win2000 Pro

0 at start
testing test.txt

---------- TEST.TXT
0 inside for loop
1 outside for loop
 
J

Joe McArthur

Matt,

You're using ERRORLEVEL incorrectly. It's not an environment variable to
be checked. It only has meaning inside the scope of the batch file's
execution. The correct usage is as follows...

if [not] ERRORLEVEL number command

ERRORLEVEL number
is true when the last program that was executed has set an exit
code? of number or higher.

HTH, Joe
 
A

Al Dunbar [MS-MVP]

Joe McArthur said:
Matt,

You're using ERRORLEVEL incorrectly. It's not an environment variable to
be checked.

It is not *exactly* an environment variable, but in w2k and xp it *can* be
treated that way. Seems like this must have been crossposted too widely...

/Al
It only has meaning inside the scope of the batch file's
execution. The correct usage is as follows...

if [not] ERRORLEVEL number command

ERRORLEVEL number
is true when the last program that was executed has set an exit
code? of number or higher.

HTH, Joe

Greetings,

Today, I was building a script and came across something I couldn't
explain. If anyone can explain the behavior I'm experiencing I would
appreciate it!

Here is the scenario, for each txt file in a directory, I'm using
looking for the string 'error' in it. Look at the code below.

I'm using a FOR to loop through the txt files and the FIND command to
search the files for 'error'.

Here is where I'm at a loss. I expect FIND will return an errorlevel
of 0 if it finds the string 'error' and errorlevel 1 if not for each
file. But, in reality, the errorlevel isn't changing until the FOR
loop is exited.

Can anyone explain this behavior?

I found if I replace,
find /I "error" test.txt
with,
find /I "error" test.txt || echo failed

I can test if each file has passed or failed...

Thanks in advance! :)
Matt Young

-----------------------
@echo off
REM: Test batch one
echo %ERRORLEVEL% at start
REM: create a text file for test
echo asdf>test.txt
for /f %%i in ('dir /b *.txt') do (
echo testing %%i
REM: find should change errorlevel to 1
find /I "error" %%i
echo %ERRORLEVEL% inside for loop
)
echo %ERRORLEVEL% outside for loop
REM: delete the testing text file
del test.txt

-----------------------
output from running the batch on WinXP Pro and Win2000 Pro

0 at start
testing test.txt

---------- TEST.TXT
0 inside for loop
1 outside for loop
 
J

Joe McArthur

It is not *exactly* an environment variable, but in w2k and xp it
*can* be treated that way. Seems like this must have been crossposted
too widely...

I'm not sure What you mean by "it can be treated that way", but the
environment variable "ERRORLEVEL" is unaffected by the running of any
programs inside a batch file, whereas the internal ERRORLEVEL is affected.

Joe
 
P

Phil Robyn

Matt said:
Greetings,

Today, I was building a script and came across something I couldn't explain.
If anyone can explain the behavior I'm experiencing I would appreciate it!

Here is the scenario, for each txt file in a directory, I'm using looking
for the string 'error' in it. Look at the code below.

I'm using a FOR to loop through the txt files and the FIND command to search
the files for 'error'.

Here is where I'm at a loss. I expect FIND will return an errorlevel of 0 if
it finds the string 'error' and errorlevel 1 if not for each file. But, in
reality, the errorlevel isn't changing until the FOR loop is exited.

Can anyone explain this behavior?

I found if I replace,
find /I "error" test.txt
with,
find /I "error" test.txt || echo failed

I can test if each file has passed or failed...

Thanks in advance! :)
Matt Young

-----------------------
@echo off
REM: Test batch one
echo %ERRORLEVEL% at start
REM: create a text file for test
echo asdf>test.txt
for /f %%i in ('dir /b *.txt') do (
echo testing %%i
REM: find should change errorlevel to 1
find /I "error" %%i
echo %ERRORLEVEL% inside for loop
)
echo %ERRORLEVEL% outside for loop
REM: delete the testing text file
del test.txt

-----------------------
output from running the batch on WinXP Pro and Win2000 Pro

0 at start
testing test.txt

---------- TEST.TXT
0 inside for loop
1 outside for loop

CMD resolves *all* the variables in statements within parentheses
before executing the statements; thus '%ERRORLEVEL%' has already
been resolved to '0' before any of the statements within the
parentheses have executed. You need to ENABLEDELAYEDEXPANSION
to solve this problem.

- - - - - - - - - - begin screen capture - - - - - - - - - -
<Win2000> c:\cmd>demo\FindHTM
File "c:\cmd\txt\htmlinpt.txt" contains the string "htm".
File "c:\cmd\txt\DOHTML.TXT" contains the string "htm".
File "c:\cmd\txt\~~temp.txt" contains the string "htm".
File "c:\cmd\txt\jobschedulers.txt" contains the string "htm".
File "c:\cmd\txt\NTUrls.txt" contains the string "htm".
File "c:\cmd\txt\dosurls.txt" contains the string "htm".
File "c:\cmd\txt\NTFS vs FAT File Systems.txt" contains the string "htm".

<Win2000> c:\cmd>rlist demo\FindHTM.cmd
=====begin c:\cmd\demo\FindHTM.cmd ====================
1. @echo off
2. setlocal ENABLEDELAYEDEXPANSION
3. for %%a in (c:\cmd\txt\*.txt) do (
4. find /i "htm" "%%a" > nul
5. if !errorlevel! equ 0 echo File "%%a" contains the string "htm".
6. )
=====end c:\cmd\demo\FindHTM.cmd ====================
- - - - - - - - - - end screen capture - - - - - - - - - -
 
D

David Wang [Msft]

Another way to get around this bizarre (but by-design) behavior of CMD shell
scripting is to use:
CALL :AnotherFunction %PARAM%

i.e.

for %%a in (c:\cmd\txt\*.txt) do (
CALL "%%a"
)
GOTO :EOF

:MyFunction
find /i "htm" "%1" > nul
if %errorlevel% equ 0 echo File "%1" contains the string "htm".
GOTO :EOF


I would also use:

SET DIR_SEARCH=C:\*
FOR /D %%I IN ( %DIR_SEARCH% ) DO (
ECHO %%I
...
)

Instead of using a FOR /F construction.

Also, there is the FINDSTR.EXE command available from W2K and up which you
would find interesting -- since it does know RegExp match, recurse, and a
couple other useful things...

--
//David
IIS
This posting is provided "AS IS" with no warranties, and confers no rights.
//
Matt said:
Greetings,

Today, I was building a script and came across something I couldn't explain.
If anyone can explain the behavior I'm experiencing I would appreciate it!

Here is the scenario, for each txt file in a directory, I'm using looking
for the string 'error' in it. Look at the code below.

I'm using a FOR to loop through the txt files and the FIND command to search
the files for 'error'.

Here is where I'm at a loss. I expect FIND will return an errorlevel of 0 if
it finds the string 'error' and errorlevel 1 if not for each file. But, in
reality, the errorlevel isn't changing until the FOR loop is exited.

Can anyone explain this behavior?

I found if I replace,
find /I "error" test.txt
with,
find /I "error" test.txt || echo failed

I can test if each file has passed or failed...

Thanks in advance! :)
Matt Young

-----------------------
@echo off
REM: Test batch one
echo %ERRORLEVEL% at start
REM: create a text file for test
echo asdf>test.txt
for /f %%i in ('dir /b *.txt') do (
echo testing %%i
REM: find should change errorlevel to 1
find /I "error" %%i
echo %ERRORLEVEL% inside for loop
)
echo %ERRORLEVEL% outside for loop
REM: delete the testing text file
del test.txt

-----------------------
output from running the batch on WinXP Pro and Win2000 Pro

0 at start
testing test.txt

---------- TEST.TXT
0 inside for loop
1 outside for loop

CMD resolves *all* the variables in statements within parentheses
before executing the statements; thus '%ERRORLEVEL%' has already
been resolved to '0' before any of the statements within the
parentheses have executed. You need to ENABLEDELAYEDEXPANSION
to solve this problem.

- - - - - - - - - - begin screen capture - - - - - - - - - -
<Win2000> c:\cmd>demo\FindHTM
File "c:\cmd\txt\htmlinpt.txt" contains the string "htm".
File "c:\cmd\txt\DOHTML.TXT" contains the string "htm".
File "c:\cmd\txt\~~temp.txt" contains the string "htm".
File "c:\cmd\txt\jobschedulers.txt" contains the string "htm".
File "c:\cmd\txt\NTUrls.txt" contains the string "htm".
File "c:\cmd\txt\dosurls.txt" contains the string "htm".
File "c:\cmd\txt\NTFS vs FAT File Systems.txt" contains the string "htm".

<Win2000> c:\cmd>rlist demo\FindHTM.cmd
=====begin c:\cmd\demo\FindHTM.cmd ====================
1. @echo off
2. setlocal ENABLEDELAYEDEXPANSION
3. for %%a in (c:\cmd\txt\*.txt) do (
4. find /i "htm" "%%a" > nul
5. if !errorlevel! equ 0 echo File "%%a" contains the string "htm".
6. )
=====end c:\cmd\demo\FindHTM.cmd ====================
- - - - - - - - - - end screen capture - - - - - - - - - -
 
G

Gary Smith

In microsoft.public.win2000.general Joe McArthur said:
I'm not sure What you mean by "it can be treated that way", but the
environment variable "ERRORLEVEL" is unaffected by the running of any
programs inside a batch file, whereas the internal ERRORLEVEL is affected.

Not so. The pseudo-environment variable ERRORLEVEL preciselt refelcts the
internal error level so long as you don't assign it an explicit value:


Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.

[D:\] ver

Microsoft Windows 2000 [Version 5.00.2195]

[D:\] echo ErrorLevel=%errorlevel%
ErrorLevel=0

[D:\] seterrl 62

[D:\] if errorlevel 62 if not errorlevel 63 echo Yes!
Yes!

[D:\] echo ErrorLevel=%errorlevel%
ErrorLevel=62
 
P

Phil Robyn

David said:
Another way to get around this bizarre (but by-design) behavior of CMD shell
scripting is to use:
CALL :AnotherFunction %PARAM%

i.e.

for %%a in (c:\cmd\txt\*.txt) do (
CALL "%%a"
)
GOTO :EOF

:MyFunction
find /i "htm" "%1" > nul
if %errorlevel% equ 0 echo File "%1" contains the string "htm".
GOTO :EOF


I would also use:

SET DIR_SEARCH=C:\*
FOR /D %%I IN ( %DIR_SEARCH% ) DO (
ECHO %%I
...
)

Instead of using a FOR /F construction.

Also, there is the FINDSTR.EXE command available from W2K and up which you
would find interesting -- since it does know RegExp match, recurse, and a
couple other useful things...

Hi, David:

I was simply trying to answer the original poster's question and explain
why CMD behaves as it does with his particular example, so I used the same
construct that he was trying unsuccessfully to use to illustrate the
behavior and show how to 'fix' it. Perhaps he will make use of your
alternative suggestions.

FINDSTR.EXE is also part of NT 4.0; it was not new with Win2000. Maybe
the original poster will also look into FINDSTR.
 
D

David Wang [Msft]

I appreciate your answers. You have the most concise answer to the stated
question; I am merely trying to point out other alternatives. I once had
the exact same question myself, and the set of answers that I was given
compelled me to at least give out the same alternatives I was given.

Oops, I just realized that it should be:

CALL :MyFunction "%%a"

instead of

CALL "%%a"

--
//David
IIS
This posting is provided "AS IS" with no warranties, and confers no rights.
//
David said:
Another way to get around this bizarre (but by-design) behavior of CMD shell
scripting is to use:
CALL :AnotherFunction %PARAM%

i.e.

for %%a in (c:\cmd\txt\*.txt) do (
CALL "%%a"
)
GOTO :EOF

:MyFunction
find /i "htm" "%1" > nul
if %errorlevel% equ 0 echo File "%1" contains the string "htm".
GOTO :EOF


I would also use:

SET DIR_SEARCH=C:\*
FOR /D %%I IN ( %DIR_SEARCH% ) DO (
ECHO %%I
...
)

Instead of using a FOR /F construction.

Also, there is the FINDSTR.EXE command available from W2K and up which you
would find interesting -- since it does know RegExp match, recurse, and a
couple other useful things...

Hi, David:

I was simply trying to answer the original poster's question and explain
why CMD behaves as it does with his particular example, so I used the same
construct that he was trying unsuccessfully to use to illustrate the
behavior and show how to 'fix' it. Perhaps he will make use of your
alternative suggestions.

FINDSTR.EXE is also part of NT 4.0; it was not new with Win2000. Maybe
the original poster will also look into FINDSTR.
 
M

Matt Young

Thanks everyone for your help! Especially for spending the time during a
weekend! :)

Phil, wonderful explanation!
David, thanks for the other workarounds (findstr & call another function)!

I’ve got the work around in place and it’s running fine. :)

Matt
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top