Why does the first line of a batch file fails to execute? - batch-file

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.

Related

Windows CMD %ERRORLEVEL% in nested IF

In a Windows 7 logon script first I want to test, if a certain file exists. If (and only if) that is the case, I want to test, if a certain registry key exists. If that is not the case, I want to do some stuff. So here's what I came up with:
IF EXIST %SOME_FILE% (
REG QUERY "HKCU\..." /v some_key
IF %ERRORLEVEL%=="1" (
do_some_stuff
)
)
The problem I'm encountering is the fact, that %ERRORLEVEL% is equal to 0, if the file exists - not if the registry key exists. If I don't do the file test, everything is fine. Now, I could use labels and goto, but I'm not really a friend of that. Is there a (simple) alternative?
Simple solution? Sure. %variable% in a code block is calculated before the code block. What you need is delayed expansion, aka command by command settings of variable contents.
Simply add this to the top of your file,
setlocal enableDelayedExpansion
and use exclamation points around variables; !errorlevel! instead of percent signs.
Although, I agree with Simon Catlin's answer completely.
Nested IFs in Windows shell script are nasty. I'd go with:
IF EXIST %SOME_FILE% (
%SystemRoot%\System32\reg.exe query hklm\system\currentcontrolset\services\wuauserv /v type 2>nul || (
echo Do something
echo Do something else
)
)
Have you considered switching to PowerShell? Note the explicit pathing for REG.EXE. This removes the risk of someone shoving a pay-loaded REG.EXE in your path.

Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?

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.

Why is only the first line of the condition executed?

I have a simple batch file in which I want to do things if an operation failed. In the condition, it seems only the first lines executes for some reason...
call "%local_path%\unins000.exe" /verysilent
IF ERRORLEVEL 1 (
echo ERROR: uninstallation failed
REM Installation failed, deletes the folder
rmdir /s /q "%local_path%"
set batcherrorlevel=1
)
IF %batcherrorlevel% neq 0 exit /b %batcherrorlevel%
If the uninstall fails, the echo works and displays, but my exit code at the end is 0. However, if I place the line "set batcherrorlevel=1" to be the first line in the condition, the exit code is 1 but the echo does not print.
EDIT: I never found the real cause of the issue, but it seems it solved iself... Bothers me a little, but as long as it works, I guess it's ok...
Sometimes batch file crashes when you put remarks inside a block statement
I think you may need to wrap it in percents, and add an == Operator.
I string-ify my comparisons, but that may just be me.
If "%ERRORLEVEL%"=="1"
Or you could look at if using a line continuation character is needed.
See http://blogs.msdn.com/b/oldnewthing/archive/2008/08/06/8835317.aspx

Can't get errorlevel from failed MOVE command in batch script

I'm pretty new to this forum so i first want to thank you for providing me with solutions even before i became a member :).
So I have this code:
for %%a in ("%PBpath%") do (
move "network location 1 files" "network location 2" >NUL
if ERRORLEVEL 0 (echo Diagram %%~na.pdf was successfuly archived) else ( echo Diagram %%~na.pdf was not archived )
ECHO.%errorlevel%
)
The problem is that I can't get the errorlevel different than 0. Even when the files that are to be copied are missing from location, i still get the successfuly archived message echoed. I searched the forum for similar questions, but i couldn't make it work for some reason.
Is there something different between the copy and the ping command (the ping command returns the correct exit code in the errorlevel), because i can't get it with either copy or move...
Thanks!
Andrew
The strange thing about the IF ERRORLEVEL statement is that it doesn't act like you expect- it returns TRUE if the errorlevel is equal to OR GREATER THAN the number specified.
A failure in MOVE sets errorlevel to 1 (I just checked) which is greater than 0. Therefore the first clause in the IF statement will always be used. The easiest way to fix your script is to reverse the conditions in the IF statement:
if ERRORLEVEL 1 (echo file was not archived) else (echo file was successfully archived)
Just use %ERRORLEVEL% variable instead of ERRORLEVEL function
If one wants to use the ERRORLEVEL function, Superbob's answer address' this (though I would recommend the form if NOT ERRORLEVEL 1 (echo file was successfully archived) else (echo file was not archived) instead).
But if one wants to use the %ERRORLEVEL% variable method instead, Delayed Expansion needs to be turned on. The OP's code above, with the recommended changes, is below:
setlocal enabledelayedexpansion
for %%a in ("%PBpath%") do (
move "network location 1 files" "network location 2" >NUL
if !ERRORLEVEL! equ 0 (
echo Diagram %%~na.pdf was successfully archived
) else (
echo Diagram %%~na.pdf was not archived)
)

Need batch file to query registry key and write computer name to a log file

I need to query the registry for HKCU\Software\test If this file exists I need to write the computer name to a log file. c:\Log.txt. I can query the registry but I have not been able to figure out how to use the if statement to add the computer name to the log file. Any help would be appreciated.
reg query "hkcu\software\test"
echo %COMPUTERNAME% >> c:\Log.txt
echo %COMPUTERNAME% >> c:\Log.txt
thats all :)
Try this:
#ECHO OFF
REG QUERY "HKCU\Software\test" >nul 2>&1
IF %ERRORLEVEL%==0 ECHO %COMPUTERNAME%>>C:\Log.txt
The >nul 2>&1 will hide the output of the REG command. If you want to see the output, just remove that part.
You could use the same approach as in this answer to your previous question, only use && in this case:
REG QUERY "whatever\you\want\to\query" >NUL && ECHO %COMPUTERNAME%>>C:\Log.txt
Similarly to FINDSTR, REG also sets ERRORLEVEL to a non-zero value if the search was unsuccessful, which allows us to use constructs with || and && as appropriate. The command after && is executed only if the search has been successful.
The above command suppresses standard output of REG with >NUL. If the search fails, the corresponding error message will still be displayed, because it is sent to the standard error device, rather than to the standard output. You can additionally suppress possible error messages by adding 2>NUL or like in #aphoria's answer.

Resources