Returning an exit code from a bash function

How to return a exit code from a function and use it in conditional?
I tried the following but it does not seem to work.

tests.sh:

if test ./load.sh ; then
    echo "0"
else
    echo "1"
fi

load.sh:

return 1;

from command line:

$ ./tests.sh
0

I was expecting it to output "1" :confused:
Thank you.

I'm confused. The return 1 statement can only be used in a function or a sourced shell script. This doesn't apply here?

Are you sure you are using bash for this? If you do not specify:

#!/bin/bash

as the very first line of test.sh then you are using

/bin/sh

which is a POSIX shell, not necessarily bash.

 echo $SHELL

will tell you ( inside test.sh) what shell you are using.

So help us to help you with some more information.

PLUS
a return code of zero means success in the shell, any other number is a fail.

PS: this is what I get for test.sh and my system

Owner@Owner-PC ~
$ chmod +x test.sh

Owner@Owner-PC ~
$ ./test.sh
./test.sh: line 2: return: can only `return' from a function or sourced script

Owner@Owner-PC ~
$ bash --version
GNU bash, version 4.4.5(1)-release (x86_64-unknown-cygwin)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

I do not want to boot to linux && have to shutdown my windows stuff right now - so cygwin.

Sorry I left out the bash header. I am using Bash.

I have several load.sh scripts, and want to call them all from one test.sh script.
Each load.sh script calls a C++ function that returns an error code (0 means EXIT_SUCCESS).
Is there a way to pass the error code from C++ function, to load.sh, to test.sh?
And then test.sh use the returned error code in a conditional statement?

Thanks.

tests.sh:

#!/bin/bash

if test ./load.sh ; then
    echo "0"
else
    echo "1"
fi

load.sh:

#!/bin/bash

return main
int main()
{
    return 1; //1 means EXIT_FAILURE
}

from command line:

$ g++ main.cpp -o main
$ ./tests.sh
0

I was expecting it to output "1".

The bash shell script:

#!/bin/bash

return main

does not make any attempt to run the utility named main . And, as jim mcnamara already said, you don't use return to exit from a bash shell script. According to the standards, using return when you are not inside a function or inside a script invoked by the . command produces unspecified results. Running that script with bash prints two diagnostic messages:

load.sh: line 2: return: main: numeric argument required
load.sh: line 2: return: can only `return' from a function or sourced script

and exits with exit code 1. Running that same script with ksh produces no diagnostics and exits with exit code 0.

If you just want a shell script that invokes a utility named main and exits with an exit code matching that returned by main , you can use any of the following:

#!/bin/bash
main
rc=$?
if [ $rc -eq 0 ]
then	exit 0
else	exit $rc
fi

or:

#!/bin/bash
main
rc=$?
if [ $rc -eq 0 ]
then	exit 0
fi
exit $rc

or:

#!/bin/bash
main
rc=$?
exit $rc

or:

#!/bin/bash
main
exit $?

or, more simply, just:

#!/bin/bash
main
1 Like

Thanks Don Cragun.

I tried the scripts you suggested in my load.sh file, but get the same result.

load.sh:

#!/bin/bash
main
rc=$?
exit $rc

From command line:

$ ./tests.sh
0

Bash is not giving may any messages when I run tests.sh. Yet jim mcnamara and you are seeing messages.
Is that something I can turn on?

However, I do get a message when I run load.sh directly:

$ ./load.sh
./load.sh: line 2: main: command not found
127
$ ls
load.sh  load.sh~  main  main.cpp  main.cpp~  tests.sh  tests.sh~

---------- Post updated at 03:20 AM ---------- Previous update was at 03:15 AM ----------

Update:

Adding "./" to load.sh fixed load.sh:

#!/bin/bash
./main
rc=$?
echo $rc
exit $rc

From terminal:

$ ./load.sh
1

But tests.sh still prints 0:

$  ./tests.sh
0

I was expecting it to output "1".

Try:

if ./load.sh ; then
    echo "0"
else
    echo "1"
fi

--
See test: operands

string
True if the string string is not the null string; otherwise, false.
1 Like

I don't see any function in the code you have posted ....

Thank you Scrutinizer, that worked. Outputs are as expected!

test.sh:

#!/bin/bash
if ./load.sh
then
    echo "0"
else
    echo "1"
fi

load.sh:

#!/bin/bash
main
rc=$?
echo $rc
exit $rc

main.cpp:

int main()
{
    return 1; //EXIT_FAILURE
}

From terminal:

$ ./tests.sh
1
1

Also works with load.sh like this:

#!/bin/bash
./main

---------- Post updated at 04:49 AM ---------- Previous update was at 04:01 AM ----------

It stopped working after changing the location of the load.sh file:

$ cat tests.sh

#!/bin/bash
if test ../tut0/load.sh
then
    echo "0"
else
    echo "1"
fi

$ cat ../tut0/load.sh

#!/bin/bash
./main
rc=$?
echo $rc
exit $rc

$ cat ../tut0/main.cpp

int main()
{
    return 1; //EXIT_FAILURE
}
$ ls ../tut0
load.sh  main  main.cpp

Bash is not warning that the load.sh file was not found.
Is there a way to make Bash throw an error when a file is not found?

What is the proper way to specify a relative path in bash?

Thank you.

In your modified example, I don't see that you would execute load.sh anywhere. What effect would you like to see? Please provide a complete transscript, showing what you are doing.

1 Like

The example in post #9 works, it has all the files in one directory.
A C++ function main(), returns an error code (1 means EXIT_FAILURE).
The error code is passed from the main() function, to load.sh, to test.sh.
And then test.sh uses the returned error code in a conditional statement.

The following example is similar, but with load.sh script in a different directory.
The files where copied from the example in post #9, and the relative paths updated.
But the output is not as expected, and Bash is not printing any error messages.

The following is a listing of the files with the new relative paths, followed by output.

$ cat tests.sh with new relative path:

#!/bin/bash
if test ../tut0/load.sh
then
    echo "0"
else
    echo "1"
fi

$ cat ../tut0/load.sh with new relative path:

#!/bin/bash
../suite/main
rc=$?
echo $rc
exit $rc

$ cat main.cpp:

int main()
{
    return 1; //EXIT_FAILURE
}

From the terminal:

$  g++ main.cpp -o main
$ ./tests.sh
0

I was expecting it to output "1" twice, as in post #9.

A top view of the directory structure:

$ cd ..
$ ls 
suite  tut0  tut1
$ ls suite
load.sh   main      main.cpp~          tests.sh
load.sh~  main.cpp  tests.sh~
$ ls tut0
load.sh  load.sh~

What is the proper way to specify a relative path in bash?

Thank you.

For debug purposes, you might want to pwd in each and every script, and also ls the relevant paths/scripts.

1 Like

Good idea. The pwd and ls outputs are as expected. But load.sh never gets called.
Why is load.sh not called?

$ cat tests.sh

#!/bin/bash
echo "in tests.sh --------"
echo "pwd:"
pwd
echo "ls:"
ls
echo "ls ../tut0:"
ls ../tut0

echo "load.sh returned:"
if test ../tut0/load.sh
then
    echo "0"
else
    echo "1"
fi

$ cat ../tut0/load.sh

#!/bin/bash
echo "in load.sh --------"
echo "pwd:"
pwd
echo "ls:"
ls
ls "ls ../suite:"
ls ../suite

../suite/main
rc=$?
echo "rc:"
echo $rc
exit $rc

From the terminal:

$ ./tests.sh
in tests.sh --------
pwd:
/home/wolfv/Documents/developer/c++/demo/test_script/suite
ls:
load.sh  load.sh~  main  main.cpp  main.cpp~  tests.sh	tests.sh~
ls ../tut0:
load.sh  load.sh~
load.sh returned:
0

I was expecting output to also contain the line "in load.sh --------".

What if you run ../tut0/load.sh without test ? And, how about setting bash 's xtrace option?

1 Like

I didn't know about xtrace. Nice. Thanks for the tip.
Running ../tut0/load.sh directly works fine.
But test.sh is not calling ../tut0/load.sh .

$ cat tests.sh

#!/bin/bash
echo "in tests.sh --------"
pwd
ls
ls ../tut0

if test ../tut0/load.sh
then
    echo "0"
else
    echo "1"
fi

$ cat ../tut0/load.sh

#!/bin/bash
echo "in load.sh --------"
pwd
ls
ls ../suite

../suite/main
rc=$?

From the terminal:

$  bash -x ./tests.sh
+ echo 'in tests.sh --------'
in tests.sh --------
+ pwd
/home/wolfv/Documents/developer/c++/demo/test_script/suite
+ ls
load.sh  load.sh~  main  main.cpp  main.cpp~  tests.sh	tests.sh~
+ ls ../tut0
load.sh  load.sh~
+ test ../tut0/load.sh
+ echo 0
0

I was expecting output to also contain the line "in load.sh --------".
Why is test.sh not calling ../tut0/load.sh ?:confused:

Running load.sh directly, pwd prints the directory it was called from, rather than the file's directory:

$  bash -x ../tut0/load.sh
+ echo 'in load.sh --------'
in load.sh --------
+ pwd
/home/wolfv/Documents/developer/c++/demo/test_script/suite
+ ls
main  main.cpp	main.cpp~  tests.sh  tests.sh~
+ ls ../suite
main  main.cpp	main.cpp~  tests.sh  tests.sh~
+ ../suite/main
in main()-------
+ rc=1
+ echo rc:
rc:
+ echo 1
1
+ exit 1

Remove the test , let if interpret the exit code of load .sh immediately.

That fixed it! :b:
Thank you RudiC and everyone else that helped along the way.
I could not have done it without you.

The following example works as intended.

$ cat tests.sh:

#!/bin/bash
echo "in tests.sh --------"
if ../tut0/load.sh
then
    echo "in tests.sh again --------"
    echo "0"
else
    echo "in tests.sh again --------"
    echo "1"
fi

$ cat ../tut0/load.sh:

#!/bin/bash
echo "in load.sh --------"
../suite/main
rc=$?
echo "rc:"
echo $rc
exit $rc

$ cat main.cpp:

#include <iostream>

int main()
{
    std::cout << "in main() -------\n";
    return 1; //EXIT_FAILURE
}

From the terminal:

$  ./tests.sh
in tests.sh --------
in load.sh --------
in main()-------
rc:
1
in tests.sh again --------
1

You don't evaluate the exit code of load.sh. For example, if you would write

if test /usr/bin/false
then
  echo yes
fi

you would also get "yes" as answer.

You should write your code as

if ../tut0/load.sh
then
  echo 0
else
  echo 1
fi