Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to abort only function on error, but do not the script?

I have the following script:

#!/bin/bash

set -e

function do_it {
    rm this-file-does-not-exit
    echo "Continuing ..."
    echo "Done ..."
    return 0
}

if do_it ; then
    echo "Success"
else
    echo "Error"
fi
echo "End of script"

Which produces:

rm: cannot remove 'this-file-does-not-exit': No such file or directory
Continuing ...
Done ...
Success
End of script

The function is not aborted because running the function in the if statement disables set -e for the function (and can not be enabled). This is not what I want.

Running the function outside the if statement, and collecting the result, is not possible, because in this situation the complete script is aborted if the function fails:

#!/bin/bash

set -e

function do_it {
    rm this-file-does-not-exit
    echo "Continuing ..."
    echo "Done ..."
    return 0
}

do_it
if $? ; then
    echo "Success"
else
    echo "Error"
fi
echo "End of script"

Produces:

rm: cannot remove 'this-file-does-not-exit': No such file or directory

I would like the following:

  • the script aborts whenever a top-level command has a failure
  • functions are aborted whenever a command has a failure. The return code of the failing command is returned as function return code
  • failure in a function does not abort the script (only the function)

This can be implemented as follows:

#!/bin/bash

set -e

function do_it {
    rm this-file-does-not-exit || return $?
    echo "Continuing ..." || return $?
    echo "Done ..." || return $?
    return 0
}

if do_it ; then
    echo "Success"
else
    echo "Error"
fi
echo "End of script"

Which produces what I want:

rm: cannot remove 'this-file-does-not-exit': No such file or directory
Error
End of script

But this makes the implementation of the function completely unreadable: each line must be followed with a || return $?

Is there another way of aborting a function, and only the function, whenever an statement in the function fails?

like image 251
volingas Avatar asked Dec 13 '19 10:12

volingas


1 Answers

You can use "pipefail" to get the return code of the last executed command in the pipe. I wouldn't say it looks much better than your code, but at least it's shorter.

#!/bin/bash

set -ep

function do_it {
    rm this-file-does-not-exit &&
    echo "Continuing ..." &&
    echo "Done ..."
    return $?
}

if do_it ; then
    echo "Success"
else
    echo "Error"
fi
echo "End of script"

In this case function will stop executing right after first failed command (rm) and the result will be the return code of the last command in the pipeline. If "rm <filename>" will be successful, both echoes should work, and the return code will be 0.

Output:

$ touch this-file-does-not-exit
$ ./script-return.sh 
Continuing ...
Done ...
Success
End of script
$ ./script-return.sh 
rm: cannot remove 'this-file-does-not-exit': No such file or directory
Error
End of script
like image 194
Maxim Norin Avatar answered Nov 15 '22 06:11

Maxim Norin