I have some Batch scripts I use for automating application build processes, most of which involve chaining commands together using the && operator. Admittedly, I'm more experienced with Linux, but based on that experience some_command && other_command should result in other_command being run iff some_command returns an exit code of 0. This answer and this answer seem to agree with that. However this appears not to be the case on Windows cmd.exe, all of the scripts run regardless of the error code of the previous.
I decided to make a simple test for this to convince myself I wasn't going insane. Consider this test.bat, which returns an exit code of 1:
#echo off
EXIT /B 1
Running test.bat && echo This shouldn't print prints 'This shouldn't print'. But since the exit code is clearly 1, echo should not be called. I've tested that the error code was actually 1 using the %errorlevel% variable, they're coming out as expected (0 before I run the script, 1 after).
On Linux I tried the same thing. Here's test.sh:
#!/bin/bash
exit 1
Running ./test.sh && echo "This shouldn't print" gives no output, exactly what I expected.
What's going on here?
(Note: OS is Windows 7 Enterprise)
You need to use call to run the batch script, like this:
call test.bat && echo This shouldn't print
Without call, the && operator does not receive the ErrorLevel returned by the batch script.
When you run a batch file from within another one, you need to use call in order to return to the calling batch file; without call, execution terminates as soon as the called batch file has finished...:
call test.bat
echo This is going to be displayed.
...but:
test.bat
echo You will never see this!
When running test.bat is involved in a command line where multiple commands are combined (using the concatenation operator &, the conditional ones && and ||, or even a block of code within parentheses ()), all the commands following test.bat are ecexuted even if call was not used. This is because the entire command line/block has already been parsed by the command interpreter.
However, when call is used, the ErrorLevel value returned by the batch file is received (which is 1 in our situation) and the following commands behave accordingly:
call test.bat & echo This is always printed.
echo And this is also always printed.
call test.bat && echo This is not printed.
call test.bat || echo But this is printed.
(
call test.bat
echo This is printed too.
echo And again this also.
)
call test.bat & if ErrorLevel 1 echo This is printed.
But without call you will get this...:
test.bat & echo This is printed.
echo But this is not!
...and...:
test.bat && echo Even this is printed!
echo Neither is this!
...and...:
test.bat || echo But this is not printed!
echo And this is not either!
...and:
(
call test.bat
echo This is printed.
echo And this as well.
)
It seems that the && and || operators receive an ErrorLevel of 0 -- even in case ErrorLevel has already been set before test.bat is executed, strangely. Also when if ErrorLevel is used, the behaviour is similar:
test.bat & if ErrorLevel 1 echo This is not printed!
...and...:
set = & rem This constitutes a syntax error.
test.bat & if ErrorLevel 1 echo This is still not printed!
Note that the commands behind test.bat execute after the batch script, even without call.
Related
Using set -e in the shell script can let the process
exit immediately if one of the commands exits with a non-zero status
However, if I want to exclude one command, so that if that specific command exits with non-zero value, the process continues.
First rule, don't use set -e. Second rule, see rule 1. The implementation of set -e has strange behavior in edge cases, and has changed through various versions of shells. It is safest not to use it. However, you can suppress it simply by making your command part of a compound command. That is, instead of
cmd_which_may_fail
you can simply do:
cmd_which_may_fail || true
Now, if cmd_which_may_fail does fail, the script will not exit.
Note that is is sometimes tempting (and you will often see code that does this) to do something like cmd_which_may_fail || echo 'command failed!' >&2, but you really should not do that. Instead, allow cmd_which_may_fail to emit a reasonable error message (most common commands do, and if you have a command that doesn't emit good error messages you might want to reconsider using it at all), since that process will have context and can write a useful error message. Command failed is generally considered a useless error message.
I usually do
command ||:
the return status of : is zero. If command returns nonzero status, then : is executed, which returns zero status. If command returns a zero status, then : is not executed. In both cases - the return status is zero, ie. success.
I've seen:
command |:
Well... it's one character shorter, but binding stdout to : process seems like a waste of cpu time for me. The return status of the pipe is the return status of the last executed command, and it's :. But this can fail when set -o pipefail, so I wouldn't recommend it.
You can run a command in a subshell:
( set +e; command; )
this allows for more flexibility, but you need to add ( before the command and is far more typing.
( set +e; command; ) do not work
( set +e; command; true ) is the solution
Example:
#!/usr/bin/bash
set -e
( set +e ; killall DUMMY_PROCESS_ABC )
echo happy end
This will work:
#!/usr/bin/bash
set -e
echo $( set +e ; killall DUMMY_PROCESS_ABC )
echo happy end
this also work:
#!/usr/bin/bash
set -e
(set +e ; killall DUMMY_PROCESS_ABC ; true)
echo happy end
I have this kind of batch file
start /b ruby script.rb && echo done
...
However, it write 'done' immediately after i run the batch file, which is incorrect, as the script took about 5 mins.
So, how to echo done only after the bg task succesfully end?
thank you!
note: I think that && operator work this way if it is not used in batch file nor in usage to run bg task.
start "" /b cmd /c "ruby script.rb && echo done"
Without the quotes the conditional execution command is seen as the continuation of the start command instead of continuation of the ruby command.
As the && is an operator handled by cmd we need to start a new instance.
The "" at the start are needed as the first quoted argument to start is handled as a window title.
note: && is an conditional execution operator. If the previous command does not generate an error then the following command is executed. In this case with the process running in background, probably, &, the command concatenation operator, should be used to know that the script ended.
I am trying to make a real .bat to .exe converter. I'm currently attempting to create a script witch injects cmd-commands into one cmd-process/environment. Here is what I have so fare:
#!/usr/bin/python3
from os import system as cmd
def ce(*a):
a = list(a)
for b in range(0, len(a)):
cmd(a[b])
# I need each entry of cmd(a[b]) to run in the same cmd environment and therefore
# do what a normal .bat/.cmd program would.
return a
cd1 = ['#echo off', 'set hw=Hi', 'echo %hw%', 'set hw', 'pause']
if ce(*cd1) != cd1:
print('Error')
cmd('pause')
quit()
All it does is iterate through those commands in 'cd1' and executes them one-by-one. As soon as I establish 'hw' as 'Hi', the variable is gone. Not only that but instead of saying "ECHO is off' it displays "%hw%". IF anyone can help me, please do so.
The problem is that you execute each line in a new cmd environment instead of executing line by line in one cmd. To get it workig you have to concatenate all lines with && and then run the whole string as one command line.
But there's still a problem. Concider this line:
#echo off && set x=123 && echo %x% && pause
This will not return the output 123 because the commands are executed at once so echo %x% won't know that %x% is set to 123. To get this working you have to add the command SETLOCAL EnableDelayedExpansion and adress the value of x by !x! instead of %x%.
So the line
#SETLOCAL EnableDelayedExpansion && #echo off && set x=123 && echo !x! && pause
will give you the desired output.
You should decide how you want your script to work. If you use a && b b will only be executed if a was successful. If you user a & b instead b will be executed even if there was an error in a.
I'm wondering if it's possible to have a batch file check itself for a string.
I'm using a batch file to run Maven commands,and I want to check if anything failed by searching for a "FAILURE" string at the end of the script
I know you can FIND in other files, but can you have it check the current output on itself, or is the best solution to save the batch file output as a text and then search it?
As an example, if I had a batch file echo Hello World it would print Hello World, then I would want to search the output for Hello and tell me it found the string Hello.
I like vane's idea to take action upon the return code provided by mvn, but instead of using ERRORLEVEL, I like to use the || operator. The commands after || are only excecuted if the prior command failed.
::initialize error flag to undefined
set "mvnErr="
::run your Maven command and define the error flag if there was an error
call mvn {your arguments here} || set mvnErr=1
::You can now take action if there was an error
if defined mvnErr echo there was a Maven error
You can do this by checking the errorlevel after each Maven command. For example
#ECHO OFF
set hasErrors=0
REM execute maven command here
if not errorlevel 0 set hasErrors=1
REM more batch command and similar checking ...
if %hasErrors%==1 (
echo print your error info or do whatever
)
Building off of both the 2 previous answers, I think this gives the best of both worlds. The || syntax is short and easy to read and the IF statement before every command ensure processing only progresses when previous operations were successful.
set hasErrors=0
IF %hasErrors%==0 call mvn -f ./mycoservices/pom.xml install || set hasErrors=1
IF %hasErrors%==0 call mvn -f ./mycostatic/pom.xml install || set hasErrors=1
IF %hasErrors%==0 call mvn -f ./mycoweb/pom.xml install || set hasErrors=1
IF %hasErrors%==0 call mvn -f ./mycowebbundle/pom.xml install || set hasErrors=1
why not just exit the batch whenever it fail
if %ERRORLEVEL% NEQ 0 (
echo exit /b %errorlevel%
exit /b %errorlevel%
)
I'm writting out some text to a text file within a cmd batch script like such:
echo FlagValue=Y>>flag.txt
This normally works fine but occassionally if the text file is open by a different process, an error messgae is returned saying Access Denied. What I'd like to do is stop the batch file if an error occurs with something like:
if return_code GEQ 1 GOTO ERR
But can't find a return code from echo command. Does one exist, or is there a better tactic to use to capture error message?
echo FlagValue=Y>>flag.txt || echo access_denied Ensure you have rights
or
echo FlagValue=Y>>flag.txt
if /i %errorlevel% NEQ 0 do (
echo access_denied Ensure you have rights
call sendmail.cmd
)
Sample:
C:\Users\Me\Desktop>echo Hello > MyFile.txt || echo ERROR
Access is denied.
ERROR
C:\Users\Me\Desktop>echo Hello > a.txt || echo ERROR
C:\Users\Me\Desktop>
Everytime you run a command the ERRORLEVEL environment variable is set to your command's return. So try echo %ERRORLEVEL% straight after you run your command. (Be careful as any command you run inbetween (including echo) will override the %ERRORLEVEL%.
Also, check these out for more information:
Can a batch file capture the exit codes of the commands it is invoking?
Batch Files - Error Handling