Running a bash function with set -e without exitin

2019-04-08 06:09发布

问题:

Consider the following function

function current_dir {
  set -e
  git foobar
  echo "I will not print this, because git foobar returned a non-zero exit code"
}

Now, when I source the function and try to call it in my shell, it exits not only the function, but also the shell itself.

How can this be avoided?

回答1:

If you don't need the function to execute in the current shell (e.g., it isn't setting any parameter values that need to be visible to the caller), you can make the body of the function a subshell, not a command group:

current_dir () (
    set -e
    git foobar
    echo "I will not print this, because git foobar returned a non-zero exit code"
)


回答2:

As far as I know, set -e will do exactly what you see: exiting the shell completely as soon as a command exits with a non-zero status.

You can try to reformulate your function with trap or using && between the commands:

function current_dir {
  git foobar && echo "I will not print this, because git foobar returned a non-zero exit code"
}

or (better readability):

function current_dir {
  trap 'trap - ERR; return' ERR
  git foobar
  ...other commands...
  echo "I will not print this, because a command returned a non-zero exit code"
}

If you really need set -e for some reason, you can temporary disable it with set +e and reenable it again after your critical section.



回答3:

"set -e" is used to exit immediatly when a command (a pipeline or a sub-shell command, etc.) exits with a non-zero status.

By default, BASH ignores errors and continues to interpret your script. This can cause a very bad surprise, an example:

    if ! lss myLock
    then touch myLock; echo "No lock, I can do the job"; rm -f myLock
    fi

The result of this script is:

bash: lss : commande introuvable
No lock, I can do the job

As you can see, BASH makes no difference between a command not found and a command which failed.

"-e" is frequently used to make sure the Shell interpretor will stop immediatly after an error (so we have to think about all errors...) This help to prevent a very bad issue when we performed a mistake in the script (think about a rm -rf "$v"/* when you forgot to set v ;-)). For this purpose we pass "-e" option in the shebang. Obviously, it's not designed for interactive use, and I don't imagine a good usage of "set -e", nor of "set +e" (but to test).

To answer to your initial question. You can avoid the termination of your shell by applying one of the following solution:

Use if statement

function myF1() {
    if git foobar; then
        echo "I will not print this, because git foobar returned a non-zero exit code"
    fi
}

Use the control operator &&

function myF2() {
    git foobar && \
    echo "I will not print this, because git foobar returned a non-zero exit code"
}

Open a subshell

function myF4() {(
    set -e
    git foobar
    echo "I will not print this, because git foobar returned a non-zero exit code"
)}

In this case, "set -e" will exit immediatly of the sub-shell, but it will not terminate the caller :-)

Use a trap is not a good idea here as it would cause a side-effect on the caller (the trap is defined globally). Obviously we can combine trap and sub-shell, but it's not useful in your case.



标签: bash shell