4

Say I have the following (pointless) Bash function:

myfunc() {
    ls
    failfailfail
    uptime
}

I run it as follows:

myfunc || echo "Something is wrong."

What I want to happen is ls runs (as it should), failfailfail does not work (since it doesn't exist), and uptime doesn't run. The return value of the function would be nonzero, and the error message would be shown. The return value doesn't have to be the exact exit status of the failed command, it just shouldn't be zero.

What actually happens is I get the output of ls, followed by "-bash: failfailfail: command not found", followed by the output of uptime. The error message is not shown, because the failed exit status is getting eaten.

set -e has no useful effect, either within the function or in the scope where the function is called. The only way I can get this to work the way I want is:

myfunc() {
    ls || return $?
    failfailfail || return $?
    uptime || return $?
}

But this seems awfully repetitive and ugly. Is there another way to clean this up?

2

Under set -e, the non-existence of failfailfail causes the whole script to exit (or the subshell, if the function is executed in a subshell).

If you don't need to modify the shell state from the function, you can run it in a subshell.

myfunc() (
    set -e
    ls
    failfailfail
    uptime
)

Another approach in bash is to set an ERR trap to execute return. If you want to make it a local setting, you need to restore the old trap value, which is a little cumbersome.

myfunc() {
    local old_ERR_trap=$(trap -p ERR)
    if [[ -z $old_ERR_trap ]]; then old_ERR_trap="trap - ERR"; fi
    trap 'local ret=$?; eval "$old_ERR_trap"; return $ret' ERR
    ls
    failfailfail
    uptime
}
0

The only way you can make it simpler is by combining it all in one command but it makes the script a bit harder to maintain than your last version:

myfunc() {
    ls && failfailfail && uptime || return $?
}
  • You can put line breaks after &&, which would make this easier to maintain IMO. You don't need backslashes for that, either. – Wildcard Apr 28 '16 at 16:31
0

Given that they are all simple commands, you could do it something like the following:

myfunc() {
  ls &&
  failfailfail &&
  uptime ||
  return $?
}
  • It's interesting but I think it may cause more confusion that way. – Julie Pelletier Apr 28 '16 at 16:35
0

Sometimes things are better put in a Makefile, which handles this sort of thing.

#!/bin/bash
myfunc() { # watch out for tabs!
        make -f - <<!
default:
        ls -d
        failfailfail
        uptime
!
}
  • This feels... devious. – smitelli May 2 '16 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.