Getting errorlevel from system command - c

I have a C project and at some point I call the system command below:
ret_val = system("#ECHO OFF\nFC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt");
FC command basically compares the two files and supposed to return an error level.
If I call this command on command prompt, I can view the error simply by echoing the errorlevel variable. I have no problem here.
My question is I would like to have this error level in an int variable in my code. I don't want it to be seen on my terminal but at the same time I want this variable in order to analyze my comparison result. This ret_val variable is always 0, it's not the functionality that I need.
How can I get errorlevel value in my code?

The system command only executes the first line of your string, so it executes #echo off, which doesn't change default return code, and you always get 0.
Of course, you could
paste the text in a .bat file and execute this .bat file
use commands chaining: #echo off && FC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt
but in your case, since you have only one command to call, just call it without the #echo off
ret_val = system("FC /A /L /N C:\\tmp\\test.out C:\\bin\\win\\output.txt")
system doesn't need echo to be turned off. Only batch file execution defaults to verbose. System calls are silent (they don't print the issued commands).

Related

batch script: The process tried to write to a nonexistent pipe

#echo off
(for /f "skip=1 tokens=3,5,11" %%a in (Data.txt) do (if /i "%%c"=="%1" (if %%b==%2 echo %%a))
echo EXIT
)|Sum.exe
I'm trying to write a simple batch script that would take the .txt file with columns of data (Data.txt), find some values using 'if' and redirect all found values to stdin input of "Sum.exe".
"EXIT" is also redirected as it means that there's no more input to be given.
When I run above code first found value is printed in console and then "The process tried to write to a nonexistent pipe" message error is printed multiple times. Therefore echo EXIT somehow must be messing up with |Sum.exe. How to properly redirect both for and echo Exit into Sum?
EDIT:
Ok, so here's the input part of the Sum program (written in c++)
std::string a;
while (a != "EXIT")
{
std::cin >> a;
if (isNumber(a))
add(sum, std::stoi(a));
}
I added cout to see whether the data was being processed and it seems that the commands in batch script were treted as input aswell.
My first suggestion would be procedure call: call :PROCEDURE. But the call doesn't work with pipe redirection.
For example call :PROCEDURE | SORT return error: Invalid attempt to call batch label outside of batch script.
So I suggest to use input parameter as switch flag to call self batch. For example it would be third parameter: %3. Batch calls itself while third parameter equals x.
So I made this code
#echo off
if "%3"=="x" (%~nx0 %1 %2|sort )& GOTO :EOF
for /f "skip=1 tokens=3,5,11" %%a in (Data.txt) do (
if /i "%%c"=="%1" (if %%b==%2 echo %%a)
echo EXIT
)
I use sort.exe utility in my example. Change it to your sum.exe
To call batch-file use syntax: my_batch.cmd [value11] [value5] x
P.S. I think you do not need word EXIT in output but I left it in example code.

Why are input values from user not working in my code in batch file? [duplicate]

This question already has answers here:
windows batch SET inside IF not working
(2 answers)
save and display user input values in .bat not working
(1 answer)
Closed 3 years ago.
Operating system is Windows 10. My batch file code is:
#echo off
:start
REM check if there are more then one arguments
if not "%2" == "" (
echo Too many parameters entered
) ELSE (
REM check if argument one is empty
if "%1"=="" (
ECHO Enter File Name Your want to edit
SET /P name=
ECHO Your Name is %name%
)
)
I am not getting code working in Set /p name= section.
On first run code works fine.
C:\Users\Ahmad khan\Desktop\work>test.bat
Enter File Name Your want to edit
asd
Your Name is asd
But when run the code again, the input section doesn't work.
Enter File Name Your want to edit
arslan
Your Name is asd
You can see on second run that I entered name Arslan, but displayed is asd as entered on first run.
The same happens on third run showing the name entered on second execution.
Enter File Name Your want to edit
qqqqq
Your Name is arslan
Your problem comes from the fact batch files evaluate commands in IF blocks at the same time. That was outlined in this Q/A. Here are some edits I brought to your code:
#echo off
:start
REM check if there are more then one argumnets
if not "%2" == "" (
echo Too many parameters entered
) ELSE (
REM check if argument one is empty
if "%1"=="" (
ECHO Enter File Name Your want to edit
SET /P name=
goto echoname
)
)
:echoname
ECHO Your Name is %name%
Now it waits for the input before displaying the variable name.
I suggest this code:
#echo off
rem Is there specified more than one argument?
if not "%~2" == "" goto ErrorArgCount
rem Is there specified as second argument just " or just ""?
set SecondArgument=%2
if not defined SecondArgumentgoto CheckFirstArg
set "SecondArgument="
:ErrorArgCount
echo ERROR: Too many parameters used on calling "%~nx0".
echo/
pause
goto :EOF
:CheckFirstArg
setlocal EnableExtensions DisableDelayedExpansion
if not "%~1" == "" set "FileName=%~1" & goto ProcessFile
:Begin
set "FileName="
set /P "FileName=File name: "
rem Has the user entered anything at all?
if not defined FileName goto Begin
rem Remove all double quotes from file name.
set "FileName=%FileName:"=%"
rem Has the user entered anything else than double quotes?
if not defined FileName goto Begin
:ProcessFile
echo Continue "%~nx0" with file "%FileName%" ...
echo/
pause
endlocal
The batch file first checks if it was called with more than one argument. The first IF condition is for typical arguments like Argument2 or "Argument 2".
But a user could run the batch file also with the arguments list "File Name" "" "third argument" or with FileName " on which first IF condition removing surrounding double quotes from second argument fails to detect the empty second argument string. For that reason the second argument string is assigned to an environment variable which is not defined anymore if there is really no second argument string. Otherwise environment variable SecondArgument is defined with string value " or "" which results in second IF condition being false and therefore running into error code for too many arguments.
Beginners in batch file writing should read carefully How does the Windows Command Interpreter (CMD.EXE) parse scripts?
It is most important to know how cmd.exe processes environment variable references using syntax %variable%, especially on using immediately expanded environment variables within a command block starting with ( and ending with ). Such a command block is parsed by Windows command processor completely with replacing all %variable% variable references by current values of the referenced environment variables before the command block is used at all. Command blocks are used usually with the commands IF, ELSE and FOR. Every environment variable defined or modified inside a command block must be referenced inside the command block using delayed expansion or the code is not working as expected by the writer of the batch file. The help/documentation output on running in a command prompt window set /? explains when and how to use delayed environment variable expansion on an IF and a FOR example.
It is good practice on batch file writing to avoid command blocks where it is easy possible by writing the code in a simple top to down manner with execution branches done using command GOTO.
There is the command START as it can be seen on running in a command prompt window help or help start or start /?. For that reason it is not good to use as label the string start. It is possible, but it is not advisable as it makes it difficult to search for start being interpreted as command in comparison to start being used as label. For that reason the code above uses Begin as label which is not a Windows command as documented by Microsoft on page Windows Commands as well as and even better by SS64 on SS64.com - A-Z index of the Windows CMD command line.
It is necessary to take care on prompting a user for an input with set /P because the user has the freedom to enter nothing at all which means the environment variable is still not defined and not being defined before or keeps its value on being already defined. The user can also enter by mistake or intentionally a string which could result in executing something for which the batch file is not written at all or Windows command processor exits batch file processing because of a syntax error caused by user input and not good written batch file code. Please read my answer on very detailed How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
See also single line with multiple commands using Windows batch file for an explanation of operator & used in batch code above to execute two commands specified on one command line.

Why does Windows command interpreter output the error message: ) was unexpected

I'm making a Minecraft modding tool using a batch file. But on execution of the batch file the Windows command interpreter outputs the syntax error message:
) was unexpected
I can't figure out why.
This is my code:
#echo off
cd mods
setlocal enabledelayedexpansion
set "selected=1"
call:print 1
call:print 2
:menu
choice /c wse>nul
if "%errorlevel%"=="2" (
if not !selected! GEQ !a! (
set /a "selected+=1"
cls
call:print 1
call:print 2
)
)
if "%errorlevel%"=="1" (
if not !selected!==1 (
set /a "selected-=1"
cls
call:print 1
call:print 2
)
)
if "%errorlevel%"=="3" (
)
goto menu
:print
if "%1"=="1"set a=0
echo.
if "%1"=="1" (
echo Uninstalled:
) else (
echo Installed:
)
echo.
for %%f in (*.jar) do (
if "%1"=="1" (
if NOT EXIST
"C:/Users/Coornhert/AppData/Roaming/.minecraft/mods/%%~nf.jar" (
set /a "a+=1"
if "!a!"=="!selected!" (
echo -%%~nf
) else (
echo %%~nf
)
set "b=!a!"
)
) else (
if EXIST "C:/Users/Coornhert/AppData/Roaming/.minecraft/mods/%%~nf.jar" (
set /a "a+=1"
if "!a!"=="!selected!" (
echo -%%~nf
) else (
echo %%~nf
)
set "b=!a!"
)
)
)
goto :eof
And it works, but when I hit s, execution terminates with the error message.
Folder structure of folder containing the batch file:
mods
Foo.jar
Foo2.jar
Folder structure of target folder:
C:\Users\Coornhert\AppData\Roaming\.minecraft\mods
Foo.jar
I partly do not understand what this batch file should do, but here is the batch file rewritten with several improvements.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem cd /D "%~dp0mods"
pushd "%~dp0mods"
set "a=0"
set "selected=1"
call :PrintIt 1
call :PrintIt 2
:Menu
choice /C wse /N
if errorlevel 3 popd & endlocal & goto :EOF
if errorlevel 2 goto AddOne
if %selected% == 1 goto Menu
set /A selected-=1
cls
call :PrintIt 1
call :PrintIt 2
goto Menu
:AddOne
if %selected% GEQ %a% goto Menu
set /A selected+=1
cls
call :PrintIt 1
call :PrintIt 2
goto Menu
:PrintIt
if %1 == 1 set "a=0"
echo/
if %1 == 1 (echo Uninstalled:) else echo Installed:
echo/
for %%I in (*.jar) do (
if %1 == 1 (
if not exist "%APPDATA%\.minecraft\mods\%%~nI.jar" (
set /A a+=1
if !a! == %selected% (echo -%%~nI) else echo %%~nI
set "b=!a!"
)
) else (
if exist "%APPDATA%\.minecraft\mods\%%~nI.jar" (
set /A a+=1
if !a! == %selected% (echo -%%~nI) else echo %%~nI
set "b=!a!"
)
)
)
goto :EOF
It does nothing useful as is, but batch code in question is also not useful at all.
The applied improvements are:
The command SETLOCAL is moved to top of file. The reason is:
It pushes path of current directory on stack.
It pushes state of command extensions on stack.
It pushes state of delayed expansion on stack.
It pushes the memory address of the current environment variables table on stack.
It creates a copy of the current environment variables table in memory and makes this new environment variables table active.
It sets command extensions and delayed expansion according to the specified parameters if the command is called with parameters at all.
The command ENDLOCAL is executed before leaving batch file. The reason is:
It deletes the current environment table which means no environment variable defined in this batch file exists anymore after ENDLOCAL except it existed already before execution of command SETLOCAL.
It pops memory address of previous environment table from stack and uses this address resulting in restoring initial environment variables.
It pops state of delayed expansion from stack and disables/enables delayed expansion accordingly.
It pops state of command extensions from stack and disables/enables command extensions accordingly.
It pops previous current directory path from stack and sets current directory to this path to restore the current directory.
So the entire command process environment is restored on exit of this batch file to exactly the same environment as it was on starting the batch file.
This makes it possible to call this batch file from within another batch file or from within a command prompt window with no impact on calling batch file or command process.
The command CD could be extended to include drive and path of argument 0 which is the full path of the batch file ending with a backslash because the subdirectory mods is most likely always expected in directory of the batch file and it should not matter what is the current directory on running the batch file.
But cd /D "%~dp0mods" could fail if the batch file is located on a network share accessed using UNC path and therefore command PUSHD is used instead working with enabled command extensions also for UNC paths.
In all programming and scripting languages it is required that variables are defined and initialized with a value before being used the first time. For that reason the environment variables a and selected are defined at top of the batch file with default values. By the way: a is a very bad name for a variable. Why? Search for a in batch file. It is quite often found on not using special find features like whole word only, isn't it.
PRINT is a command as it can be seen on running in a command prompt window print /?. While it is possible to use command names as labels or as names for subroutines, it is not advisable to do so as it could be confusing for readers of the batch file.
The command CHOICE has the option /N to hide the list of choices in the prompt. It is better to use this option than redirecting the output of CHOICE to device NUL.
The very old but still valid Microsoft support article Testing for a Specific Error Level in Batch Files explains that if errorlevel X means that the condition is true if the exit code of previous command or application is greater or equal X. The command CHOICE with 3 choices exits always with 1, 2 or 3 as exit code. So it is best to use:
if errorlevel 3 rem Do something on third choice avoiding fall through to next line.
if errorlevel 2 rem Do something on second choice avoiding fall through to next line.
Do something on first choice.
The advantage of using this method is that it even works with CHOICE within a command block on which if %ERRORLEVEL% == X fails because of delayed expansion would be required and usage of if !ERRORLEVEL! == X.
The integer comparison if %selected% GEQ %a% would not work if the two arguments would be enclosed in double quotes as the double quotes are also interpreted as part of the arguments to compare. For that reason using if "%selected%" GEQ "%a%" would result in running a string comparison instead of an integer comparison. For more information about comparing values with IF look on answer on Exit a for loop in batch.
It is safe here to omit the double quotes also on the other IF conditions with == operator running string comparisons because the environment variables selected and a must be both defined before running this IF condition and therefore both variables are defined at top of the batch file.
The answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains why set "variable=value" should be always used to assign a value to an environment variable or delete an environment variable on omitting the value. And this answer also explains why on set /A variable=expression the double quotes can be usually omitted as whitespace characters are interpreted completely different within an arithmetic expression. The exception is usage of set /A with 1 or more commands on same command line on which double quotes around variable=expression would be also needed.
The batch file should be exited when the batch file user enters e or E to take third choice. This could be done with just goto :EOF, or with exit /B which is an alias for goto :EOF, or with just exit which always exits entire command process independent on calling hierarchy which is not recommended. Windows command interpreter would implicitly restore the initial stack before finishing batch file execution. But it is nevertheless good coding practice to pop from stack with code which was pushed on stack before with code. For that reason there is used popd & endlocal & goto :EOF. See answer on Single line with multiple commands using Windows batch file for more information about usage of multiple commands on one command line.
The list of predefined environment variables of used user account is output on running in a command prompt window the command set. One predefined Windows environment variable is APPDATA with path to application data of current user account. This environment variable should be used instead of a fixed path to application data directory of user account.
And the directory separator on Windows is the backslash character \ and not slash character / as on Unix and Mac.
The usage of f as loop variable is not recommended as this is also a loop variable modifier. %%~f can be interpreted by Windows command interpreter as value of loop variable f without surrounding double quotes or as incomplete loop variable reference because of missing loop variable after %%~f which could be also interpreted as full file name of ?. So it is better to use # or $ as loop variable or upper case letters to avoid such a confusion on interpreting the loop variable reference. Loop variables are case-sensitive.
I prefer for IF conditions with an ELSE branch the coding style
if condition (
command
) else (
command
)
But here in this batch file with command being just a short ECHO command the code is better readable on being more compact with using:
if condition (echo short message) else echo other short message
Delayed expansion for an environment variable referenced within a command block started with ( and ending with matching ) is only needed if the environment variable is also modified in same command block. Therefore environment variable a must be referenced in body of FOR with usage of delayed expansion while environment variable selected can be referenced as usual because of not modified within this command block at all.
It is better to use echo/ to output an empty line instead of echo.. For the reason read the DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/
For a basic understanding of the used commands, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
choice /?
cls /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
rem /?
set /?
setlocal /?

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.

Is redirection recognized as a argument when executing a script/program?

I have a batch-file program that gets called from another batch-file so I can redirect error output to a file. When you run the commmand START /B "C:\Some\Script" 2>"C:\Some\Log.log" Is the redirection recognized as an argument?
Just a question with no real usefulness (at least that I can tell), just asking out of curiousity.
No, it doesn't. Try using this as your test script to verify:
set argC=0
for %%x in (%*) do Set /A argC+=1
echo %argC%
In that file, argC is the number of arguments. (Source: Wikibooks, via this answer.)
Ben is correct - redirection is not counted as an argument. The redirection is handled before the script is called.
As an alternative to counting the args as a test, you can simply look at the args that have been passed in to your script
echo %*
Another option when you use START or CMD /C is to echo %CMDCMDLINE% to see the exact line that was used to launch CMD (including the parameters). It is really handy sometimes to help diagnose parsing, escape and expansion issues. This is also handy when the instantiation of CMD is implicit such as with FOR /F %%A IN ('command') or command1|command2 (each side of the pipe gets its own CMD session).
I'm curious why you are using START /B script 2>logFile instead of CALL script 2>logFile? They both should work, but I think the 2nd option is simpler and more efficient.

Resources