In our current setup build process (WiX 3.9), certain files get digitally signed. To avoid signing an unchange file again, I want to check each file for a signature and skip signing if it's already signed.
I've tried using signtool.exe verify /pa filename.exe to check if the file is already signed, and signtool returns with a nonzero ERRORLEVEL if there is no signature. I thought I could check the error code after the call and handle it appropriately:
signtool.exe verify /pa %1
IF ERRORLEVEL 0 goto already_signed
rem Sign file now
[...]
goto finished
:already_signed
echo File %1 is already signed, skipping
:finished
This works fine if a signature is found and signtool returns 0. But if no signature is found, resulting in a nonzero ERRORLEVEL, MSBuild takes immediate notice of that and displays an error message: EXEC : SignTool error : No signature found. One step later, the build fails due to a -1 return code from the signing batch file. In terms of the build process however, there were no errors that would have to be treated like ones.
I've already tried to reset the ERRORLEVEL to 0 after the signtool verify call, but that doesn't work. Any ideas?
As S.T. suggests above, simply add exit /b 0 below your :finished label. If you want to reset %ERRORLEVEL% without exiting your script, you can do cmd /c exit /b 0, then %ERRORLEVEL% will be reset to zero and your script will continue.
Just so I feel like I put some effort into this answer, I'll offer an unrelated tip. :)
Another neat trick for testing exit code status is conditional execution.
>NUL 2>&1 signtool.exe verify /pa "%~1" && (
echo File %1 is already signed, skipping
) || (
rem Sign file now
[...]
exit /b 0
)
The >NUL 2>&1 stuff simply hides all stdout and stderr output of signtool.exe. The code block after && fires if signtool exits 0. Otherwise, the code block after || fires.
Related
I am trying to understand why the first line of my batch file fails to execute. My code is as follows:
if exist reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion" goto OptionOne
exit
:OptionOne
some code
goto:eof
It never goes to the OptionOne subroutine. Instead, it just exits.
I do have a solution to this problem written differently (so I don't want examples to make it work) but I want to understand why this one line fails to execute.
Is the syntax improper? Google says it is correct.
Poorly designed code? I know this registry key exists so this is not the case.
Is it something with the return value and its correct syntax, but needs to be further written out on the else statements?
The code you have doesn't work because if exist is used only to check if folders or files exist. Its syntax is:
if exist "C:\foldername\" (do something) else (optionally do something else)
for folders and:
if exist "C:\filename" (do something) else (optionally do something else)
for files.
My suggested solution (as mentioned in comments) is the following:
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion" >nul 2>&1
if %errorlevel% EQU 0 (goto :OptionOne) else (echo Registry key not found. & pause>nul & exit /b 1)
:OptionOne
some code
goto :eof
which checks if the command returned errorlevel different than equal to 1 or bigger (the registry key exists) or 1 or bigger (it doesn't exist).
REG QUERY only returns 0 for success or 1 for failure. Note that no results is still a successful query operation and will return 0.
Ref: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/reg-query
And as commentors noted, IF EXIST is only for files and folders, not for commands.
Just launch reg query and check %errorlevel%, as you can see here:
Prompt>reg query "HKCU\..." (put something which exists)
<some successful answers>
Prompt>echo %errorlevel%
0
Prompt>reg query "blabla"
ERROR: Invalid key name.
Type "REG QUERY /?" for usage.
Prompt>echo %errorlevel%
1
You can check %errorlevel% in your batch script.
A frequent method to handling errors within Windows batch scripts is to use things like
if errorlevel 1 ... or if %errorlevel% neq 0 .... Often times one wants the error handling code to preserve the ERRORLEVEL.
I believe all external commands will always result in ERRORLEVEL being set to some value, so the error handling code must preserve the ERRORLEVEL in an environment variable prior to executing an external command.
But what about internal commands? The problem is, some internal commands clear the ERRORLEVEL to 0 when they succeed, and some do not. And I can't find any documentation specifying which commands do what.
So the question is, which internal commands clear the ERRORLEVEL to 0 upon success? This is not a general question about returned ERRORLEVEL codes, but strictly about success results.
There are posts like What is the easiest way to reset ERRORLEVEL to zero? and Windows batch files: .bat vs .cmd? that give partial answers. But I have never seen a comprehensive list.
Note: I've been curious about this for years. So I finally decided to run a bunch of experiments and come up with a definitive answer. I'm posting this Q&A to share what I have found.
This answer is based on experiments I ran under Windows 10. I doubt there are differences with earlier Windows versions that use cmd.exe, but it is possible.
Also note - This answer does not attempt to document the ERRORLEVEL result when an internal command encounters an error (except for a wee bit concerning DEL and ERASE)
Not only are there difference between commands, but a single command can behave differently depending on whether it was run from the command line, or within a batch script with a .bat extension, or from within a batch script with a .cmd extension.
The following set of commands never clear the ERRORLEVEL to 0 upon success, regardless of context, but instead preserve the prior ERRORLEVEL:
BREAK
CLS
ECHO
ENDLOCAL
FOR : Obviously, commands in the DO clause may set the ERRORLEVEL, but a successful FOR with at least one iteration does not set the ERRORLEVEL to 0 on its own.
GOTO
IF : Obviously, commands executed by IF may set the ERRORLEVEL, but a successful IF does not set ERRORLEVEL to 0 on its own.
KEYS
PAUSE
POPD
RD
REM
RMDIR
SHIFT
START
TITLE
The next set of commands always clear the ERRORLEVEL to 0 upon success, regardless of context:
CD
CHDIR
COLOR
COPY
DATE
DEL : Always clears ERRORLEVEL, even if the DEL fails (except when run without any file argument).
DIR
ERASE : Always clears ERRORLEVEL, even if ERASE fails. (except when run without any file argument).
MD
MKDIR
MKLINK
MOVE
PUSHD
REN
RENAME
SETLOCAL
TIME
TYPE
VER
VERIFY
VOL
Then there are these commands that do not clear ERRORLEVEL upon success if issued from the command line or within a script with a .bat extension, but do clear the ERRORLEVEL to 0 if issued from a script with a .cmd extension. See https://stackoverflow.com/a/148991/1012053 and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J for more info.
ASSOC
DPATH
FTYPE
PATH
PROMPT
SET
Lastly, there are these commands that do not fit neatly into any of the prior categories:
CALL : If a :routine or batch script is CALLed, then ERRORLEVEL is exclusively controlled by the CALLed script or :routine. But any other type of successful CALL to a command will always clear ERRORLEVEL to 0 if the CALLed command does not otherwise set it.
Example: call echo OK.
EXIT : If used without /B, then the cmd.exe session terminates and there is no more ERRORLEVEL, just the cmd.exe return code. Obviously EXIT /B 0 clears the ERRORLEVEL to 0, but EXIT /B without a value preserves the prior ERRORLEVEL.
I believe that accounts for all internal commands, unless there is an undocumented command that I missed.
Your description of CALL command is incomplete:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it.
Example: call echo OK.
Check this small example:
#echo off
call :setTwo
echo Set two: %errorlevel%
call :preserve
echo Preserve: %errorlevel%
call echo Reset
echo Reset: %errorlevel%
call :subNotExists 2> NUL
echo Sub not exist: %errorlevel%
goto :EOF
:setTwo
exit /B 2
:preserve
echo Preserve
exit /B
Output:
Set two: 2
Preserve
Preserve: 2
Reset
Reset: 0
Sub not exist: 1
CALL description should say something like this:
CALL : Clears ERRORLEVEL if the CALLed command does not otherwise set it. Example: call echo OK, but if the called command is a subroutine it preserves the prior ERRORLEVEL. If the called subroutine does not exist, it sets the ERRORLEVEL to 1.
Trying my hands on windows batch files, in the below code that I found by searching in www.
#ECHO OFF
REM Call this with two arguments, and it will add them.
SET a=%1+%2
IF %ERRORLEVEL%==0 (goto errors-0) ELSE (goto errors-1)
REM Instead of using goto with the variable, this uses an IF-ELSE structure
:errors-0
REM This is if it was successful
ECHO %a%
goto exit
:errors-1
REM this is if it had an error:
ECHO Errors occurred.
goto exit
REM GUESS WHAT, THIS REM WILL NEVER EVER BE READ! IT WILL BE SKIPPED OVER BY THE GOTOS
:exit
ECHO.
ECHO press any key to exit.
PAUSE>nul
The code is suppose to take 2 arguments, add them and echo the result.
But this won't execute with success on my Windows 8.1 machine. Below is what I get:
C:\ProjectDoc>add3.bat
Errors occurred.
press any key to exit.
So, U added an echo for the ERRORLEVEL to see its value after executing the command SET. Below is the output:
C:\ProjectDoc>add3.bat 2 3
9009
Errors occurred.
press any key to exit.
C:\ProjectDoc>
So, is this errorlevel in Windows equal to the $? of Linux. Should it be returning 0 for every successful execution of a command or is it different? Reading some docs relates it to the Linux $? but it isn't clearly working as $? in Linux.
Yes, to be precise, %errorlevel% is analogous to $? in Bash shell.
In your batch file, SET a=%1+%2 is not doing what you expect it to do. It just sets the value of the variable a to the string "2+3" assuming you ran the file with arguments 2 3. If you want to do arithmetic you need to use the /A option: set /a a=%1+%2.
The SET command (and many other built-in commands) only set the ERRORLEVEL if there has actually been an error. If it was successful, the ERRORLEVEL will retain its previous value. I think this is what you're witnessing in your question.
By contrast, when a command runs an executable file, when the process completes it always sets the ERRORLEVEL.
As well as checking the ERRORLEVEL variable for specific values, it is idiomatic (for historical reasons) to check the errorlevel using the following expression
IF ERRORLEVEL 1 ECHO Hello
This will run the given command if ERRORLEVEL is 1 or above - in other words, if any error has occurred.
So I need to compare two text files and if there is a difference in content in one of them then tell the batch file to GOTO Diffrence I know that the FC command can check diffrences but can I use it to make it goto a diffrent place
so I run
fc %cd%\ActiveVer.txt %cd%\currentver.txt
ActiveVer.txt says:
0.5.6
and currentver.txt says:
0.5.7
fc tells me the difference.
But I'm trying to see and make it run GOTO out-of-date if there is a difference and do echo You are up to date! if there is none.
Should I run another command to do this or is there something that allows me to do that with fc?
Most commands return an error code upon completion. By convention, zero equates to success, and non-zero equates to failure (this is a general rule - there are exceptions). So most of this answer can be applied to any command, as long as you know how to interpret the returned error code.
The FC command returns 0 if the files match, and 1 it there is at least one difference. You don't need to see the output of the command (the differences), so you can redirect stdout to nul.
One option is to use IF ERRORLEVEL N, which evaluates to true if the returned error code is >= N.
fc ActiveVer.txt CurrentVer.txt >nul
if errorlevel 1 goto outOfDate
echo you are Up-To-Date
exit /b
:outOfDate
echo you are Out-Of-Date
exit /b
Note that %cd%\file and file are equivalent - the %cd% is not needed.
Another option is to check for a specific value by using the dynamic %ERRORLEVEL% "variable".
fc ActiveVer.txt CurrentVer.txt >nul
if %errorlevel%==1 goto outOfDate
echo you are Up-To-Date
exit /b
:outOfDate
echo you are Out-Of-Date
exit /b
But I almost never use either syntax above. Instead I use the conditional command concatenation operators && and ||. Commands after && only execute if the prior command returned zero, and commands after || execute if the command returned non-zero. Note that commands after && might fail, which could cause the || commands to fire, even if the original command succeeded. For this reason, it is a good idea to end your && commands with a command that is guaranteed to succeed. A good choice is (call ), which does nothing other than return 0 (success).
someCommand && (
REM Success commands go here
REM Make sure the last commmand in this block returns 0
(call )
) || (
REM Error commands go here
)
You simply want to GOTO if FC "fails" (finds a difference), so you only need the ||.
fc ActiveVer.txt CurrentVer.txt >nul || goto outOfDate
echo You are Up-To-Date
exit /b
:outOfDate
echo You are Out-Of-Date
I'm currently writing my first batch file for deploying an asp.net solution.
I've been Googling a bit for a general error handling approach and can't find anything really useful.
Basically if any thing goes wrong I want to stop and print out what went wrong.
Can anyone give me any pointers?
I generally find the conditional command concatenation operators much more convenient than ERRORLEVEL.
yourCommand && (
echo yourCommand was successful
) || (
echo yourCommand failed
)
There is one complication you should be aware of. The error branch will fire if the last command in the success branch raises an error.
yourCommand && (
someCommandThatMayFail
) || (
echo This will fire if yourCommand or someCommandThatMayFail raises an error
)
The fix is to insert a harmless command that is guaranteed to succeed at the end of the success branch. I like to use (call ), which does nothing except set the ERRORLEVEL to 0. There is a corollary (call) that does nothing except set the ERRORLEVEL to 1.
yourCommand && (
someCommandThatMayFail
(call )
) || (
echo This can only fire if yourCommand raises an error
)
See Foolproof way to check for nonzero (error) return code in windows batch file for examples of the intricacies needed when using ERRORLEVEL to detect errors.
Using ERRORLEVEL when it's available is the easiest option. However, if you're calling an external program to perform some task, and it doesn't return proper codes, you can pipe the output to 'find' and check the errorlevel from that.
c:\mypath\myexe.exe | find "ERROR" >nul2>nul
if not ERRORLEVEL 1 (
echo. Uh oh, something bad happened
exit /b 1
)
Or to give more info about what happened
c:\mypath\myexe.exe 2&1> myexe.log
find "Invalid File" "myexe.log" >nul2>nul && echo.Invalid File error in Myexe.exe && exit /b 1
find "Error 0x12345678" "myexe.log" >nul2>nul && echo.Myexe.exe was unable to contact server x && exit /b 1
Other than ERRORLEVEL, batch files have no error handling. You'd want to look at a more powerful scripting language. I've been moving code to PowerShell.
The ability to easily use .Net assemblies and methods was one of the major reasons I started with PowerShell. The improved error handling was another. The fact that Microsoft is now requiring all of its server programs (Exchange, SQL Server etc) to be PowerShell drivable was pure icing on the cake.
Right now, it looks like any time invested in learning and using PowerShell will be time well spent.
A successful ping on your local network can be trapped using ERRORLEVEL.
#ECHO OFF
PING 10.0.0.123
IF ERRORLEVEL 1 GOTO NOT-THERE
ECHO IP ADDRESS EXISTS
PAUSE
EXIT
:NOT-THERE
ECHO IP ADDRESS NOT NOT EXIST
PAUSE
EXIT
I guess this feature was added since the OP but for future reference errors that would output in the command window can be redirected to a file independent of the standard output
command 1> file - Write the standard output of command to file
command 2> file - Write the standard error of command to file
Python Unittest, Bat process Error Codes:
if __name__ == "__main__":
test_suite = unittest.TestSuite()
test_suite.addTest(RunTestCases("test_aggregationCount_001"))
runner = unittest.TextTestRunner()
result = runner.run(test_suite)
# result = unittest.TextTestRunner().run(test_suite)
if result.wasSuccessful():
print("############### Test Successful! ###############")
sys.exit(1)
else:
print("############### Test Failed! ###############")
sys.exit()
Bat codes:
#echo off
for /l %%a in (1,1,2) do (
testcase_test.py && (
echo Error found. Waiting here...
pause
) || (
echo This time of test is ok.
)
)
Its extremely easy!
Create a file that contains:
call <filename> // the file you made
cls
echo An error occured!
<Your commands>
pause
So now when you start it, it will launch your program as normal. But when anything goes wrong it exits and continues the script inside the first file. Now there you can put your own commands in.