I have read this, which suggests the following compilation check:
installCheck () {
if g++ check_opencv.cpp -o check_opencv; then
return 1
else
rm check_opencv
return 0
fi
}
if installCheck $0; then
echo "OpenCV already installed, skipping"
exit 0
fi
However, running this script gives:
check_opencv.cpp:2:33: fatal error: opencv2/core/core.hpp: No such file or directory
compilation terminated.
rm: check_opencv: No such file or directory
OpenCV already installed, skipping
which is wrong. Then I change to this:
installCheck () {
g++ check_opencv.cpp -o check_opencv
if [[ $? -ne 0 ]]; then
and it works fine:
check_opencv.cpp:2:33: fatal error: opencv2/core/core.hpp: No such file or directory
compilation terminated.
Cloning into 'opencv-2.4.9'...
Why is that? Because of some behavior of [[ or g++?
Someone in this says that "Sadly as it turns out make returns 0 weather or not it fails." Shouldn't g++ always return non-zero value when it fails? Maybe I miss something obvious, so please clarify for me. Thank you very much!
Update: it turns out that I understand bash wrongly. A return value of 0 evaluates to true in bash, which is contradictory to what I thought before. This helps.
if g++ check_opencv.cpp -o check_opencv; then
return 1
will return failure (1) if the compilation succeeds. Apparently, you wanted to return failure if the compilation failed, which would be:
if ! g++ check_opencv.cpp -o check_opencv; then
return 1
Or you could just put the true and false branches in the correct order:
installCheck () {
if g++ check_opencv.cpp -o check_opencv; then
rm check_opencv
return 0
else
return 1
fi
}
I don't see anywhere in the linked question where it suggests the code which you proposed.
I suppose that your intention was to avoid contaminating the filesystem if the compilation succeeds, since the compilation itself is the only test needed. You could also achieve this with:
installCheck () {
g++ check_opencv.cpp -o check_opencv
local rv=$?
rm -f check_opencv
return rv
}
if cmd; ... is "true" if cmd is successful i.e. exit status of cmd is 0 - it is logically equivalent to cmd; if [[ $? -eq 0 ]]; ...
Related
I want to include some conditional statement into a makefile:
SHELL=/bin/bash
all:
$(g++ -Wall main.cpp othersrc.cpp -o hello)
#if [[ $? -ne -1 ]]; then \
echo "Compile failed!"; \
exit 1; \
fi
But get an error:
/bin/bash: -c: line 0: conditional binary operator expected /bin/bash:
-c: line 0: syntax error near -1' /bin/bash: -c: line 0:if [[ -ne -1 ]]; then \' makefile:3: recipe for target 'all' failed make: *** [all] Error 1
How to fix it?
Note that each line of a makefile recipe runs in a different shell, so that $? of the previous line is unavailable, unless you use .ONESHELL option.
A fix without .ONESHELL:
all: hello
.PHONY: all
hello: main.cpp othersrc.cpp
g++ -o $# -Wall main.cpp othersrc.cpp && echo "Compile succeeded." || (echo "Compile failed!"; false)
With .ONESHELL:
all: hello
.PHONY: all
SHELL:=/bin/bash
.ONESHELL:
hello:
#echo "g++ -o $# -Wall main.cpp othersrc.cpp"
g++ -o $# -Wall main.cpp othersrc.cpp
if [[ $$? -eq 0 ]]; then
echo "Compile succeded!"
else
echo "Compile failed!"
exit 1
fi
When $ needs to be passed into a shell command it must be quoted as $$ in the makefile (make charges you a dollar for passing one dollar, basically). Hence $$?.
im new to bash scripting and tring to make a script
goal: reciving 2 names (1 - logfilename 2- program name) the program should compile the program
and send both outputs to a log
if success then write "compile V" and return 0 else compile X and return number
i tried
#!/bin/bash
gcc {$2}.c -Wall -g -o $2> $1 2>&1
exit
and i have no idea how to check if it did or didnt success and the to echo V or X
edit:
thx for you guys, i got this
#!/bin/bash
gcc {$2}.c -Wall -g -o ${2}>${1} 2>&1
if (($?==0));then
echo Compile V
[else
echo compile X]
fi
exit
but all the if parts are still not working...
You can check exit status gcc like this:
#!/bin/bash
# execute gcc command
gcc "$2".c -Wall -g -o "$2"> "$1" 2>&1
# grab exit status of gcc
ret=$?
# write appropriate message as per return status value
((ret == 0)) && echo "compile V" || echo "compile X"
# return the exit status of gcc
exit $ret
You can check the success of a program in bash by command $? if echo $? = 0 then success else fail.
this code should work :
#!/bin/bash
gcc -v ${2}.c -Wall -g -o ${2}>${1} 2>&1
exit
Try this out:
#!/bin/bash
gcc "$2"".c" -Wall -g -o "$2" 2>&1 >"$1"
#check for error of previous command
if $? ; then echo compile V >>"$1"
else echo compile X >>"$1"; fi
exit
I have a makefile rule in while I am executing a linux tool. I need to check the exit status of the tool command, and if that command fails the make has to be aborted.
I tried checking with $?, $$? \$? etc in the makefile. But they gives me syntax error when makefile runs.
What is the right way to do this ?
Here is the relevant rule in Makefile
mycommand \
if [ $$? -ne 0 ]; \
then \
echo "mycommand failed"; \
false; \
fi
In the makefile-:
mycommand || (echo "mycommand failed $$?"; exit 1)
Each line in the makefile action invokes a new shell - the error must be checked in the action line where the command failed.
If mycommand fails the logic branches to the echo statement then exits.
Here are a couple of other approaches:
shell & .SHELLSTATUS
some_recipe:
#echo $(shell echo 'doing stuff'; exit 123)
#echo 'command exited with $(.SHELLSTATUS)'
#exit $(.SHELLSTATUS)
Output:
$ make some_recipe
doing stuff
command exited with 123
make: *** [Makefile:4: some_recipe] Error 123
It does have the caveat that the shell command output isn't streamed, so you just end up with a dump to stdout when it finishes.
$?
some_recipe:
#echo 'doing stuff'; sh -c 'exit 123';\
EXIT_CODE=$$?;\
echo "command exited with $$EXIT_CODE";\
exit $$EXIT_CODE
Or, a bit easier to read:
.ONESHELL:
some_recipe:
#echo 'doing stuff'; sh -c 'exit 123'
#EXIT_CODE=$$?
#echo "command exited with $$EXIT_CODE"
#exit $$EXIT_CODE
Output:
$ make some_recipe
doing stuff
command exited with 123
make: *** [Makefile:2: some_recipe] Error 123
It's essentially one string of commands, executed in the same shell.
If all you want is for the make to be aborted iff the tool exits with a nonzero status, make will already do that by default.
Example Makefile:
a: b
#echo making $#
b:
#echo making $#
#false
#echo already failed
.
This is what happens with my make:
$ make
making b
make: *** [Makefile:6: b] Error 1
Make sure partially or wholly created targets are removed in case you fail.
For instance, this
a: b
#gena $+ > $#
b:
#genb > $#
is incorrect: if on the first try, genb fails, it will probably leave an incorrect b, which, on the second try, make will assume is correct. So you need to do something like
a: b
#gena $+ > $# || { rm $#; exit 1; }
b:
#genb > $#
To those who can't still fix it, the original snippet in the question missed a semicolon after mycommand. So, the working example is:
mycommand; \ # <<== here's the missing semicolon
if [ $$? -ne 0 ]; \
then \
echo "mycommand failed"; \
false; \
fi
I wrote I simple bash script to wget an archive from iblocklist.com and extract it to my transmission blocklists dir. After it failed to run several times I found that the .gz archive being pushed by iblocklist was corrupt but the .zip was not so I decided to implement some error catching and an alternate means of accomplishing the task. After rewriting the script, I get unexpected EOF error and I cannot find where the problem lies. I'm by no means an advanced user of bash but I can usually accomplish what I want through trial and error and google. Not today. I've looked for the obvious missing }, fi, and ;'s but it looks good to me. Not sure if it matters but on this machine I'm running a Backtrack linux distro that more or less forces you to be root at all times. I'm a beginner so please be gentle :)
#!/bin/bash
function test {
"$#"
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "error with $1";
fi
return $STATUS
}
function askyn {
read -p "The operation failed. Try alternate means? [Y/n] " -n 1 -r
if [[ "$REPLY" =~ ^[Yy] ]] || [[ "$REPLY" = "" ]]; then YN=1;
else YN=0; fi
return $YN
}
function cleanup {
if [ $ALT == 0 ]; then {
test rm /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then {
echo Removal of archive failed
}fi
}else {
test rm /root/scripts/.lvl1/dl/level1.zip
if [ $STATUS -ne 0 ]; then {
echo Removal of archive failed
}fi
}fi
return
}
ALT=0
YN=-1
test wget "http://list.iblocklist.com/?list=bt_level1&fileformat=p2p&archiveformat=gz" -O /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then { #wget failed first try
askyn
if [ $YN == 1 ]; then ALT=1;else exit;fi #prompt for alternate; exit if not
}else { #wget worked first try
test file-roller -e /root/.config/transmission/blocklists /root/scripts/.lvl1/dl/level1.gz
if [ $STATUS -ne 0 ]; then { #file-roller failed to extract the list
askyn
if [ $YN == 1 ]; then ALT=1;else exit;fi #prompt for alternate; exit if not
}else { #everything worked first try
echo Download and extraction successful
cleanup
}fi
}fi
if [ $ALT == 1 ]; then { #try to wget .zip
test wget "http://list.iblocklist.com/?list=bt_level1&fileformat=p2p&archiveformat=zip" -O /root/scripts/.lvl1/dl/level1.zip
if [ $STATUS -ne 0 ]; then { #wget of .zip failed
echo Alternate means failed. Exiting.
exit
}else { #wget of .zip worked
test unzip -o -d /root/.config/transmission/blocklists /root/scripts/.lvl1/dl/level1.zip #try to unzip .zip
if [ $STATUS -ne 0 ]; then { #unzip failed
echo Alternate means failed. Exiting.
exit
}else { #everything worked second try
echo Download and extraction successful using alternate means
cleanup
}fi
}fi
}fi
The problem is your use of }fi to terminate an if. The token }fi is not a fi. So the shell at the end of the file has a lot of open ifs missing their fis.
You are in dire need of reading the shell manual, understanding shell grammar, and asking your local shell guru. The script as written is fubar, even with the syntax errors corrected.
Using braces as you have is a slightly unusual style, but there's nothing wrong with it. However, the shell is a bit picky about whitespace:
if cmd; then {
: commands
} else {
: other commands
} fi
Note the space between } and fi.
Also, you are working way too hard. Instead of explicitly checking $? all the time and writing error messages, just do things like:
if rm /root/scripts/.lvl1/dl/level1.gz; then
: # perform some commands if the remove succeeded
else
: # perform some commands on failure, but DO NOT PRINT AN ERROR MESSAGE
fi
The rational for not printing an error message is that the rm command should have already printed one. If all you plan on doing is exiting when a command fails, you can simplify things and just do:
rm /p/a/t/h || exit 1
and you can simplify even further by just doing:
#!/bin/sh -e
rm /p/a/t/h
By setting -e, the shell will immediately exit whenever any command fails.
This is following question.
I have Makefile.real (Makefile from prev question):
all: a b
a:
echo a
exit 1
b:
echo b start
sleep 1
echo b end
Now I want to create Makefile that is simple wrap of Makefile.real:
It calls make with Makefile.real with the same args as it was called
It should print error message id Makefile.real fails
This is my goal - print error message in the end of parallel make
(see question)
Therefore following commands should terminate with error message:
make -j1 a b (1)
make -j2 a b (2)
I suspect Makefile should be something close to:
%:
$(MAKE) -f Makefile.real $(MAKECMDGOALS); \
res=$$?; if [ $$res != 0 ]; then echo "Failed!!!"; fi; exit $$res
The problem is that target '%' will be called twice for a and b for (2).
Any ideas?
This is the solution I ended with
ifneq ($(REAL_MAKE),1)
# run_make will be called once (it's .PHONY target),
# even if make is called with several targets
%: run_make
#:
.PHONY: run_make
run_make:
$(MAKE) $(MAKECMDGOALS) REAL_MAKE=1; \
if [ $$? -ne 0 ]; then \
echo "*** Error ***" >&2; \
exit 1; \
fi
else # REAL_MAKE defined (actual makefile)
### HERE comes original make we want to wrap ###
endif # # REAL_MAKE defined (actual makefile)