7

I know that set -e is my friend in order to exit on error. But what to do if the script is sourced, e.g. a function is executed from console? I don't want to get the console closed on error, I just want to stop the script and display the error-message.

Do I need to check the $? of each command by hand to make that possible ?

Here an example-script myScript.sh to show the problem:

#!/bin/sh
set -e

copySomeStuff()
{
    source="$1"
    dest="$2"
    cp -rt "$source" "$dest"
    return 0
}

installStuff()
{
    dest="$1"
    copySomeStuff dir1 "$dest"
    copySomeStuff dir2 "$dest"
    copySomeStuff nonExistingDirectory "$dest"
}

The script is used like that:

$ source myScript.sh
$ installStuff

This will just close down the console. The error displayed by cp is lost.

6
  • 1
    Are you sure it would close down the console? I tried to run the script as you described and it terminates the whole things throwing cp error and exiting with 1 exit code. – ddnomad Jul 29 '16 at 9:34
  • 1
    @ddnomad The set -e sets the errexit option in the shell. When calling the function, the cp fails and the shell exits for me. – Kusalananda Jul 29 '16 at 9:59
  • 1
    If you source that it will ignore the #! and will apply set -e to the calling shell, so will apply to all subsequent commands. – ctrl-alt-delor Jul 29 '16 at 10:31
  • Double quote your user input. Don't use () when calling shell functions. – Kusalananda Jul 29 '16 at 12:29
  • ${dest} and ${source} are still unquoted, and they contain user input. I'm being a bit picky with you here, but you need to quote them in your script on your machine, or simple things like copying files with spaces in their names won't work. Sorry. I'm just trying to give constructive feedback. – Kusalananda Jul 29 '16 at 12:55
6

I would recommend having one script that you run as a sub-shell, possibly sourcing a file to read in function definitions. Let that script set the errexit shell option for itself.

When you use source from the command line, "the script" is effectively your interactive shell. Exiting means terminating the shell session. There are possibly ways around this, but the best option, if you wanted to set errexit for a session, would be to simply have:

#!/bin/bash

set -o errexit

source file_with_functions
do_things_using_functions

Additional benefit: Will not pollute the interactive session with functions.

2
  • Ok, that does the job, thanks! It's a more readable solution and probably should be preferred in most cases. However for my special use-case it would result in 6 files, since I provide different services there, currently implemented as functions, and I would like to stick to 1 file for other reason. – Alex Jul 29 '16 at 12:43
  • unfortunately that doesn't work for me - I'm calling a function defined in my shell-rc file, so I can't reasonably execute it from somewhere else – Xerus May 17 '20 at 19:03
4

If you source that, it will ignore the #! and will apply set -e to the calling shell, so will apply to all subsequent commands.

you could force a sub-shell:

copySomeStuff()
{
    (
        set -e
        source="$1"
        dest="$2"
        cp -r "$source" "$dest"
        return 0
    )
}

Also for safety:

  • Do not use all caps variable name, as likely to collide with environment names. (we had a question last week about why a script did not work, it had a variable PATH)

  • Use quotes.

  • Always use -t or -T option for cp, mv, ln. e.g.

    • cp -t destination_directory source_files …
    • cp -T source_file destination_file

    note that -t and -T are Gnu extensions, and not available on some other Unixes, so for portability you can in place of the -t option do:

    • cp source_files … destination_directory/

    I do not know of an alternate safe form for -T

6
  • 2
    Just a note about cp -t, it's a GNU extension not available on BSD systems. If GNU coreutils is installed, GNU cp may be available as gcp. – Kusalananda Jul 29 '16 at 10:47
  • 1
    set -e in the sub-shell works great, thanks ! As well thanks for the safety-hints. – Alex Jul 29 '16 at 11:54
  • Thanks for this solution, it is perfect. It also ensures that set -e is not set in the calling shell – geedoubleya Jul 4 '19 at 9:40
  • subshell works, except that the stdout and stderr of the subshell seem to be lost :/ – Xerus May 17 '20 at 19:02
  • @Xerus you will have to ask a new question, showing an example of what you did. Subsshell does not loose stdout or stderr. – ctrl-alt-delor May 18 '20 at 7: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.