Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running a bash function with set -e without exiting the shell

Tags:

bash

shell

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?

like image 834
Niels B. Avatar asked May 22 '14 12:05

Niels B.


People also ask

Does Bash script need exit at the end?

Often when writing Bash scripts, you will need to terminate the script when a certain condition is met or to take action based on the exit code of a command.

What is $@ in bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc.

Which command causes a Bash script to stop running and exit the shell?

One of the many known methods to exit a bash script while writing is the simple shortcut key, i.e., “Ctrl+X”. While at run time, you can exit the code using “Ctrl+Z”.

What does \r do in bash?

The -r tests if the file exists and if you have read permission on the file. Bash scripting tutorial - if statement. The meaning of -r depends on what program/command it is given as an argument for. In this case it is for [ , and means to check whether the file named by the next argument is readable.


3 Answers

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"
)
like image 164
chepner Avatar answered Nov 15 '22 15:11

chepner


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.

like image 22
Eric Fournie Avatar answered Nov 15 '22 13:11

Eric Fournie


"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.

like image 42
mcoolive Avatar answered Nov 15 '22 14:11

mcoolive