File redirection in Windows and %errorlevel% - windows

Lets say we want to create an empty file in windows with the following command:
type nul > C:\does\not\exist\file.txt
the directory does not exist, so we get the error:
The system cannot find the path specified
If you print out the %errorlevel% the output is:
echo %errorlevel%
0
Yet the command was not successful!
I noticed, that windows does not set the %errorlevel% of the last command if you use redirection..
Is there a way around this?

You can use the following:
C:\>type nul > C:\does\not\exist\file.txt && echo ok || echo fail
The system cannot find the path specified.
fail
C:\>echo %errorlevel%
1
I always assumed the && and || operators used ERRORLEVEL, but apparently not.
Very curious that ERRORLEVEL is set after redirection error only if you use the || operator. I never would have guessed. Nor would I ever have bothered to test if not for your excellent question.
If all you want to do is set the ERRORLEVEL upon redirection failure, then of course you can simply do:
type nul > C:\does\not\exist\file.txt || rem

The command
type nul > C:\does\not\exist\file.txt
invoked with a non-existent path is terminated at redirection failure and type is not invoked at all. It therefore has no chance of setting ERRORLEVEL. The redirection, being performed by the shell, does not set ERRORLEVEL.
One solution is to pre-initalise ERRORLEVEL with a non-zero value. It will remain unchanged upon failure and will be reset to zero (by type) upon success:
#echo off
::pre-initialise ERRORLEVEL with a value of 1:
call :SETERROR 1
type NUL > NOSUCHDIR\test.txt
IF ERRORLEVEL 1 goto ERROR
echo All is well.
goto END
:ERROR
echo Error detected.
:END
goto :eof
:SETERROR
exit /b %1
The shorft form
type NUL > NOSUCHDIR\test.txt && goto OK || goto ERROR
works because it analyses exit code, which is not the same as error level:
An exit code can be detected directly with redirection operators (Success/Failure ignoring the ERRORLEVEL) this can often be more reliable than trusting the ERRORLEVEL, which may or may not have been set correctly.
Herbert Kleebauer explained this to me in the Usenet group alt.msdos.batch.
Update:
An anonymous user suggested an alternative solution based on the COPY command:
COPY NUL: C:\does\not\exist\file.txt
This command does set ERRORLEVEL, which may be analysed by the next command in the script. Very convenient, so I thank him for the proposed edit.

Related

Clear or reset ERRORLEVEL in a Windows batch script [duplicate]

I have a post-build event that runs some commands for a c# project. The last command would sometimes cause the ERRORLEVEL value not equals to zero and then the build fails.
I want to append an extra line of command to always set the ERRORLEVEL value to zero. What is the most convenient way to do that?
if you use exit /b 0 you can return an errorlevel 0 from within a child batch script without also exiting the parent.
Seems to do the trick:
ver > nul
Not everything works, and it is not clear why. For example, the following do not:
echo. > nul
cls > nul
In a pre- or post-build event, if the return code of an executable is greater than zero, and the call to the executable is not the last line of the pre- or post-build event, a quick way to mute it and avoid triggering a check for a non-zero errorlevel is to follow the failing line with a line that explicitly returns zero:
cmd /c "exit /b 0"
This is essentially a generic combination of the previously-mentioned solutions that will work with more than just the last line of a pre- or post-build event.
I personally use this:
cd .
Works even in unix shell.
But, this one might be a bit faster:
type nul>nul
Because Process Monitor shows QueryDirectory calls on cd .
PS:
cd . has another nice side effect in the unix shell. It does restore recreated working directory in the terminal if it has been opened before the erase.
Update:
And that is a bit more faster:
call;
Any windows command to find if a file is in use or not ? :https://www.dostips.com/forum/viewtopic.php?t=5542
I found that "exit 0" looks like a good way to deal with this problem.
Usage Example:
NET STOP UnderDevService /Y
exit 0
if the UnderDevService service is not started.
I use VERIFY or VERIFY > nul
If this is a snippet like "Post-build Event" etc., then you'll be fine appending:
(...) || ver > nul
at the end of the last command.
Alternatively
cmd /c "exit /b 0"
is very clean and non-idiomatic -- a reader who knows Windows shell will know what's going on, and what was your intent.
However, if you're in a batch script, you may want to use subrotines, which are a lightweight equivalent of the "child batch script" from akf's answer.
Have a subroutine:
:reset_error
exit /b 0
and then just
call :reset_error
wherever you need it.
Here's a complete example:
#echo off
rem *** main ***
call :raise_error
echo After :raise_error ERRORLEVEL = %ERRORLEVEL%
call :empty
echo After :empty ERRORLEVEL = %ERRORLEVEL%
call :reset_error
echo After :reset_error ERRORLEVEL = %ERRORLEVEL%
:: this is needed at the end of the main body of the script
goto:eof
rem *** subroutines ***
:empty
goto:eof
:raise_error
exit /b 1
:reset_error
exit /b 0
Which outputs:
After :raise_error ERRORLEVEL = 1
After :empty ERRORLEVEL = 1
After :reset_error ERRORLEVEL = 0
As you see - just calling and returning via goto:eof is not enough.
The following works in modern Windows (NT-based) systems that feature cmd.exe:
rem /* This clears `ErrorLevel`; the SPACE can actually be replaced by an
rem arbitrary sequence of SPACE, TAB, `,`, `;`, `=`, NBSP, VTAB, FF: */
(call )
The SPACE (or more precisely, an arbitrary sequence of one or more standard token separators, which are SPACE (code 0x20), TAB (code 0x09), ,, ;, =, NBSP (code 0xFF), VTAB (code 0x0B) and FF (code 0x0C)) is mandatory; if you omit it the ErrorLevel becomes set instead:
rem // This sets `ErrorLevel` to `1`:
(call)
There is a nice thread on DosTips.com where this technique came up.
Here is an alternative method, but which accesses the file system and might therefore be a bit slower:
dir > nul
rem /* Perhaps this is a little faster as a specific file is given rather
rem than just the current directory (`.` implicitly) like above: */
dir /B "%ComSpec%" > nul
Here are some other ways to reset the ErrorLevel state, which even work in MS-DOS (at least for version 6.22):
more < nul > nul
rem // The `> nul` part can be omitted in Windows but is needed in MS-DOS to avoid a line-break to be returned:
sort < nul > nul
The following methods work in MS-DOS only:
command /? > nul
fc nul nul > nul
keyb > nul
For the sake of completeness, this sets the ErrorLevel state to 1, valid for both Windows and MS-DOS:
< nul find ""
After reviewing all of the other answers, I decided to find which way was the most efficient for resetting the ERRORLEVEL. I made a quick script that recorded the time to execute each of these:
"cmd /c "exit /b 0"", "cd .", "ver", "type nul", and "VERIFY"
here is the output:
cmd /v:on /c set ^"q=^"^" & timeit.cmd "cmd /c ^!q^!exit /b 0^!q^!" "cd ." "ver" "type nul" "VERIFY"
cmd /c "exit /b 0" took 0:0:0.02 (0.02s total)
cd . took 0:0:0.00 (0.00s total)
Microsoft Windows [Version 10.0.18362.836]
ver took 0:0:0.00 (0.00s total)
type nul took 0:0:0.00 (0.00s total)
VERIFY is off.
VERIFY took 0:0:0.00 (0.00s total)
This took 0:0:0.06 (0.06s total)
after reviewing with Measure-Command {command} in Powershell, I found that it only really accepted cd . and cmd /c "exit /b 0" --am I doing something wrong?
I'd recommend either cd . or type nul since neither have a footprint on the output of the console, nor are they slow in any measure.
yeah I'm pretty bored
Add >nul after each command that's likely to fail - this seems to prevent the build from failing.
You can still check the result of the command by examining %errorlevel%.
For example:
findstr "foo" c:\temp.txt>nul & if %errorlevel% EQU 0 (echo found it) else (echo didn't find it)
I'm using this:
ping localhost -n 1 >null
I always just used;
set ERRORLEVEL=0
I've been using it for donkey's years.

Windows batch - return failure EXITCODE but do NOT abandon script on failed instruction

I would like to ask you if you could hint me to a somehow more elegant solution (than the one provided at the end of this post) to this problem: I want to run a batch script which, if (at least) one of its instructions fails, will continue running the other instructions, but will return an errorlevel indicating a failure (say, 1).
One solution which I tested (and works) is the following:
set instructionfailed=0
**INSTRUCTION THAT WILL FAIL (ex. copy a a)**
if %errorlevel% == 1 set instructionfailed=1
**INSTRUCTION THAT WILL SUCCEED (ex.(supposing abcdefg does not exist) mkdir abcdefg) **
if %errorlevel% == 1 set instructionfailed=1
exit /b %instructionfailed%
EDIT: I have actually read (here, for example) that maybe I should use ERRORLEVEL instead of %errorcode%, but the code I have to integrate this with also seems to use %errorcode%.
There is one exceptionally simple technique that may work, depending on which commands are executed, and another slightly more complex technique that will always work.
1) Simple solution that may or may not work, depending on the commands
Some internal commands only set the ERRORLEVEL if and only if there was an error, and some always set the ERRORLEVEL upon success or error. External commands always set the ERRORLEVEL.
So if all of your commands are internal commands that do not clear the ERRORLEVEL upon success, then you can simply clear the ERRORLEVEL at the start, and then run each command in succession. The ERRORLEVEL will only be non-zero (indicating an error) if any one of the commands failed. You can simply issue EXIT /B at the end, and the current ERRORLEVEL will be returned.
An arcane but quick way to clear the ERRORLEVEL to 0 at the start is to use (call ) - the trailing space is critical.
(call )
internalCommand1
internalCommand2
...
internalCommandN
exit /b
A list of internal commands that can work with this solution may be found at Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?. Again, for this solution to work, you want the internal commands that do not clear the ERRORLEVEL upon success.
2) Slightly more complex solution that always works
Just as with your current solution, you set your own error variable to 0 at the start, and conditionally set it to non zero upon error after each command. But instead of using if errorlevel 1 or if %errorlevel% neq 0, you can use the conditional || command concatenation operator, which only executes the command if the preceding one failed.
set "err=0"
anyCommand1 || set "err=1"
anyCommand2 || set "err=1"
...
anyCommandN || set "err=1"
exit /b %err%

Why does cmd run my second command when I use && even though the first one failed?

I'm experimenting with Rust. I want to compile a program, and only if it succeeds, run it. So I'm trying:
rustc hello.rs && hello
But hello.exe always runs, even if compilation fails.
If I try
rustc hello.rs
echo Exit Code is %errorlevel%
I get "Exit Code is 101".
As I understand it, the only truthy value is 0 in cmd, which 101 is clearly not, and && is lazily evaluated, so why does it run hello?
rustc.bat looks like this:
#echo off
SET DIR=%~dp0%
cmd /c "%DIR%..\lib\rust.0.11.20140519\bin\rustc.exe %*"
exit /b %ERRORLEVEL%
Very curious this. Put a CALL in front and all should be fine.
call rustc hello.rs && hello
I don't totally understand the mechanism. I know that && and || do not read the dynamic %errorlevel% value directly, but operate at some lower level. They conditionally fire based on the outcome of the most recently executed command, regardless of the current %errorlevel% value. The || can even fire for a failure that does not set the %errorlevel%! See File redirection in Windows and %errorlevel% and batch: Exit code for "rd" is 0 on error as well for examples.
Your rustc is a batch file, and the behavior changes depending on if CALL was used or not. Without CALL, the && and || operators respond only to whether the command ran or not - they ignore the exit code of the script. With CALL, they properly respond to the exit code of the script, in addition to responding if the script failed to run (perhaps the script doesn't exist).
Put another way, batch scripts only notify && and || operators about the exit code if they were launched via CALL.
UPDATE
Upon reading foxidrive's (now deleted) answer more carefully, I realize the situation is more complicated.
If CALL is used, then everything works as expected - && and || respond to the ERRORLEVEL returned by the script. ERRORLEVEL may be set to 1 early on in the script, and as long as no subsequent script command clears the error, the returned ERRORLEVEL of 1 will be properly reported to && and ||.
If CALL is not used, then && and || respond to the errorcode of the last executed command in the script. An early command in the script might set ERRORLEVEL to 1. But if the last command is an ECHO statement that executes properly, then && and || respond to the success of the ECHO command instead of the ERRORLEVEL of 1 returned by the script.
The real killer is that EXIT /B 1 does not report the ERRORLEVEL to && or || unless the script was invoked via CALL. The conditional operators detect that the EXIT command executed successfully, and ignore the returned ERRORLEVEL!
The expected behavior can be achieved if the last command executed by the script is:
cmd /c exit %errorlevel%
This will properly report the returned ERRORLEVEL to && and ||, regardless whether the script was invoked by CALL or not.
Here are some test scripts that demonstrate what I mean.
test1.bat
#echo off
:: This gives the correct result regardless if CALL is used or not
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
test2.bat
#echo off
:: This only gives the correct result if CALL is used
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem This command interferes with && or || seeing the returned errorlevel if no CALL
test3.bat
#echo off
:: This only gives the correct result if CALL is used
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem Ending with EXIT /B does not help
exit /b %errorlevel%
test4.bat
#echo off
:: This gives the correct result regardless if CALL is used or not
:: First clear the ERRORLEVEL
(call )
:: Now set ERRORLEVEL to 1
(call)
rem The command below solves the problem if it is the last command in script
cmd /c exit %errorlevel%
Now test with and without CALL:
>cmd /v:on
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
>test1&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>test2&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Success, yet errorlevel=1
>test3&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Success, yet errorlevel=1
>test4&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>call test1&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>call test2&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>call test3&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>call test4&&echo Success, yet errorlevel=!errorlevel!||echo Failure with errorlevel=!errorlevel!
Failure with errorlevel=1
>
Quick demo to prove the point:
#ECHO OFF
SETLOCAL
CALL q24983584s 0&&ECHO "part one"
ECHO done one
CALL q24983584s 101&&ECHO "part two"
ECHO done two
GOTO :EOF
where q24983584s.bat is
#ECHO OFF
SETLOCAL
EXIT /b %1
GOTO :EOF
works as expected...

Parse the output of a command only when it's an error, and only with native batch commands

I'm writing a batch file that is calling a command, and I want to handle any errors that might result. I have as a requirement that I use only the native CLI commands; in other words, I know I could easily accomplish this if I were using PowerShell or whatever, but I'm not.
What I want is if an error occurs, check the output, and if the error is expected, continue processing, otherwise abort. However, I'm having trouble parsing the output in a useful way.
This seems to be as close as I can get:
command | (find /i "valid" >NUL && goto :okay || goto :stop)
Here, I'm piping the output of the command to find. The trouble is, this occurs regardless of the exit status of the command - it sends both the success and failure messages. One option would be:
command 1>NUL 2>(find /i "valid" >NUL && goto :okay || goto :stop)
However, this doesn't work, because it's attempting to redirect to a file; the find command doesn't receive the input.
(For testing purposes, command is a cmd file that echos a message, and exit /b 1 for an error)
Any ideas?
Based on what you list as known
On success, the errorlevel = 0
On error, the errorlevel = 1.
On error, an error message is written to the STDERR (2) stream.
On an "EXPECTED" error, the word "valid" is output.
Try this:
command 2>&1 1>nul | ( find /i "valid" >nul && goto okay || goto stop )
NOTE: the order in which the output re-directions take place is important!
CMD parses the redirects from left to right. This is directing STDERR to STDOUT and then directing STDOUT to NUL. Think of them like pointers. STDERR is getting the address of STDOUT before we set STDOUT to NULL.
Update
On error, an error message is written to the STDOUT (1) stream.
Option A:
call command > "%Temp%\out.txt" && goto okay || ( find /i "valid" "%Temp%\out.txt" >nul && goto okay || goto stop )
Option B:
( call command && echo valid ) | find /i "valid" >nul && goto okay || goto stop

What is the easiest way to reset ERRORLEVEL to zero?

I have a post-build event that runs some commands for a c# project. The last command would sometimes cause the ERRORLEVEL value not equals to zero and then the build fails.
I want to append an extra line of command to always set the ERRORLEVEL value to zero. What is the most convenient way to do that?
if you use exit /b 0 you can return an errorlevel 0 from within a child batch script without also exiting the parent.
Seems to do the trick:
ver > nul
Not everything works, and it is not clear why. For example, the following do not:
echo. > nul
cls > nul
In a pre- or post-build event, if the return code of an executable is greater than zero, and the call to the executable is not the last line of the pre- or post-build event, a quick way to mute it and avoid triggering a check for a non-zero errorlevel is to follow the failing line with a line that explicitly returns zero:
cmd /c "exit /b 0"
This is essentially a generic combination of the previously-mentioned solutions that will work with more than just the last line of a pre- or post-build event.
I personally use this:
cd .
Works even in unix shell.
But, this one might be a bit faster:
type nul>nul
Because Process Monitor shows QueryDirectory calls on cd .
PS:
cd . has another nice side effect in the unix shell. It does restore recreated working directory in the terminal if it has been opened before the erase.
Update:
And that is a bit more faster:
call;
Any windows command to find if a file is in use or not ? :https://www.dostips.com/forum/viewtopic.php?t=5542
I found that "exit 0" looks like a good way to deal with this problem.
Usage Example:
NET STOP UnderDevService /Y
exit 0
if the UnderDevService service is not started.
I use VERIFY or VERIFY > nul
If this is a snippet like "Post-build Event" etc., then you'll be fine appending:
(...) || ver > nul
at the end of the last command.
Alternatively
cmd /c "exit /b 0"
is very clean and non-idiomatic -- a reader who knows Windows shell will know what's going on, and what was your intent.
However, if you're in a batch script, you may want to use subrotines, which are a lightweight equivalent of the "child batch script" from akf's answer.
Have a subroutine:
:reset_error
exit /b 0
and then just
call :reset_error
wherever you need it.
Here's a complete example:
#echo off
rem *** main ***
call :raise_error
echo After :raise_error ERRORLEVEL = %ERRORLEVEL%
call :empty
echo After :empty ERRORLEVEL = %ERRORLEVEL%
call :reset_error
echo After :reset_error ERRORLEVEL = %ERRORLEVEL%
:: this is needed at the end of the main body of the script
goto:eof
rem *** subroutines ***
:empty
goto:eof
:raise_error
exit /b 1
:reset_error
exit /b 0
Which outputs:
After :raise_error ERRORLEVEL = 1
After :empty ERRORLEVEL = 1
After :reset_error ERRORLEVEL = 0
As you see - just calling and returning via goto:eof is not enough.
The following works in modern Windows (NT-based) systems that feature cmd.exe:
rem /* This clears `ErrorLevel`; the SPACE can actually be replaced by an
rem arbitrary sequence of SPACE, TAB, `,`, `;`, `=`, NBSP, VTAB, FF: */
(call )
The SPACE (or more precisely, an arbitrary sequence of one or more standard token separators, which are SPACE (code 0x20), TAB (code 0x09), ,, ;, =, NBSP (code 0xFF), VTAB (code 0x0B) and FF (code 0x0C)) is mandatory; if you omit it the ErrorLevel becomes set instead:
rem // This sets `ErrorLevel` to `1`:
(call)
There is a nice thread on DosTips.com where this technique came up.
Here is an alternative method, but which accesses the file system and might therefore be a bit slower:
dir > nul
rem /* Perhaps this is a little faster as a specific file is given rather
rem than just the current directory (`.` implicitly) like above: */
dir /B "%ComSpec%" > nul
Here are some other ways to reset the ErrorLevel state, which even work in MS-DOS (at least for version 6.22):
more < nul > nul
rem // The `> nul` part can be omitted in Windows but is needed in MS-DOS to avoid a line-break to be returned:
sort < nul > nul
The following methods work in MS-DOS only:
command /? > nul
fc nul nul > nul
keyb > nul
For the sake of completeness, this sets the ErrorLevel state to 1, valid for both Windows and MS-DOS:
< nul find ""
After reviewing all of the other answers, I decided to find which way was the most efficient for resetting the ERRORLEVEL. I made a quick script that recorded the time to execute each of these:
"cmd /c "exit /b 0"", "cd .", "ver", "type nul", and "VERIFY"
here is the output:
cmd /v:on /c set ^"q=^"^" & timeit.cmd "cmd /c ^!q^!exit /b 0^!q^!" "cd ." "ver" "type nul" "VERIFY"
cmd /c "exit /b 0" took 0:0:0.02 (0.02s total)
cd . took 0:0:0.00 (0.00s total)
Microsoft Windows [Version 10.0.18362.836]
ver took 0:0:0.00 (0.00s total)
type nul took 0:0:0.00 (0.00s total)
VERIFY is off.
VERIFY took 0:0:0.00 (0.00s total)
This took 0:0:0.06 (0.06s total)
after reviewing with Measure-Command {command} in Powershell, I found that it only really accepted cd . and cmd /c "exit /b 0" --am I doing something wrong?
I'd recommend either cd . or type nul since neither have a footprint on the output of the console, nor are they slow in any measure.
yeah I'm pretty bored
Add >nul after each command that's likely to fail - this seems to prevent the build from failing.
You can still check the result of the command by examining %errorlevel%.
For example:
findstr "foo" c:\temp.txt>nul & if %errorlevel% EQU 0 (echo found it) else (echo didn't find it)
I'm using this:
ping localhost -n 1 >null
I always just used;
set ERRORLEVEL=0
I've been using it for donkey's years.

Resources