32

I'm sourcing a bash script in the terminal, so exiting on error with

set -o errexit 

kills my terminal, which is EXTREMELY ANNOYING, because I have to close the terminal, open another one, and reset some variables.

So far, using

command || return 

lines, in the script, is doing exactly what I want

set -o errexit 

to do... But I want it done for the entire script; not just one line/command

I have a file full of commands for setting up a site, and I'd rather not do command || return

for every single line in the file

Is there another set option, or something else that will just "return" instead of exiting the terminal?

-- Just for clarity, I'd like to kill the script, and leave the terminal in the same state that pressing ctrl+C to kill a service running in the terminal would. command || return does that. But I don't want to tack on || return to every line in the file. So I'm looking for something similar to set -o errexit, that doesn't cause the terminal to shut down

--- Note: Creating a dumb script with two lines in it (super.sh):

create_path=~/Desktop/site_builder/create.sh source $create_path blah 

And placing set -o errexit at the top of create.sh,

works exactly as I expect it. However, it's really stupid to have to create a file with two lines in it, just to call another bash script, instead of just calling it from the terminal. Ugghhh

here's some examples:

in super.sh

#!/bin/bash create_path=~/Desktop/site_builder/create.sh source $create_path blah 

in create.sh

#!/bin/bash set -o errexit #line below this is a line that fails and will cause the script to stop and return to the terminal as expected sed "s/@@SITE_NAME@@/$dirname" ~/Desktop/site_builder/template_files/base.html > ~/Desktop/$dirname/templates/base.html # a line with a stupid error 

in the terminal:

 $ bash super.sh 

output as expected:

my-mac$ 

This works. What an annoying solution.

I want , ideally, to execute what's in the stupid super.sh file from the terminal, not the super.sh file :D, without having the terminal shut down on me. This is what happens with what I'm trying to do:

terminal command:

my-mac$ source $create_path blah 

in create.sh I still have set -o errexit

Here's the output on the terminal

 sed: 1: "s/@@SITE_NAME@@/blah": unterminated substitute in regular expression Saving session... ...copying shared history... ...saving history...truncating history files... ...completed. [Process completed] 

And then the terminal is frozen. Ctrl+C doesn't work, neither does Ctrl+D

If instead of set -o errexit , if I just use command || return statements everywhere in the create.sh file, then I get exactly what I want , while executing the lines in supser.sh directly on the terminal (instead of calling super.sh from the terminal). But that's not a practical solution either.

Note: I liked @terdon 's answer about just spawning a child shell so I ended up just spawning a sub shell via the script instead of the terminal, as he showed in his answer using the braces ( ), around the entire script.. His answer works too.

4
  • Are you doing this in a script or manually ?
    – terdon
    CommentedJul 27, 2017 at 17:16
  • I'm calling the file with source in the terminal. As in : source $file_path argument The script is being executed in the same shell I called it from (what source does, I've been told...and acts like that's the case) command || return statements are in the file I'm executing in the terminal
    – user243760
    CommentedJul 27, 2017 at 17:21
  • OK, and where is the set run? Is that in the sourced script or do you run it manually? This would be much easier to answer if you could add a simple example we can copy and try out. I have an idea, but I need a way of testing to be sure it works.
    – terdon
    CommentedJul 27, 2017 at 17:42
  • I added a Note to the original post that might help with that. But I'll also do as you suggested.
    – user243760
    CommentedJul 27, 2017 at 17:49

4 Answers 4

11

Simply source the file with a failsafe:

source the-source-file || true 

... then the overall command won't fail, even if source does.

7
  • Actually, I thought this was what I wanted, but turns out my terminal was just being slow... I actually want to return to the terminal on error, (meaning stop the entire script in it's tracks).. .for whatever reason, source file || true doesn't do this, neither does source file || return when I type this into the terminal...
    – user243760
    CommentedJul 27, 2017 at 16:56
  • Sourcing the file doesn't return you to a shell prompt??
    – Jeff Schaller
    CommentedJul 27, 2017 at 16:59
  • this is what's in my terminal: Jills-MBP:~ jillr$ source ~/Desktop/site_builder/create.sh blah || true It simply executes the next part of the script on fail, so does return instead of true. The only thing that has worked is command || return statements in the actual file for all commands, which is silly. I don't know if throwing it all in a function then calling function || return at the end of the file would do any good either.
    – user243760
    CommentedJul 27, 2017 at 17:03
  • Well, if you explicitly put || return on a command that fails, the yes, it'll return. I thought we were trying to keep your main/parent shell from exiting?
    – Jeff Schaller
    CommentedJul 27, 2017 at 17:07
  • Yes, I want to keep the terminal from actually exiting. Because I don't want to exit the terminal. I want to exit the script. Return on individual commands in the script exits the script and leaves my terminal in the same condition as pressing Ctrl+C to kill a process, such as running the server form the terminal, would. I want to avoid writing command || return for every line in the file :D
    – user243760
    CommentedJul 27, 2017 at 17:11
8

This is the only thing that works for what I needed to accomplish (creating a virtual environment then activating it, then installing requirements from a bash script):

spawn a subshell / child shell from the script, as in:

stupid_file.sh

( set -o errexit #bunch of commands #one line fails ) 

run the stupid_file using:

source stupid_file.sh <file arguments here> || true 

THE END.

** takes a bow **

(credit goes to Jeff and Terdon)

6
  • 1
    This isn't really any different than just executing the script instead of sourcing it.
    – chepner
    CommentedJul 27, 2017 at 19:22
  • @chepner hmmm. Cool. I'll have to try calling just bash $create_path blah and see if it still exits, and executes the same way, and still installs stuff into my virtual environment correctly. Out of time now.
    – user243760
    CommentedJul 27, 2017 at 19:30
  • 1
    I'll save you the time: it won't (if by "installs stuff" you means set environment variables in your current shell), and neither will (...), since all the assignments in the parentheses only effect that subshell.foo=3; (foo=5); echo "$foo" will output 3, not 5.
    – chepner
    CommentedJul 27, 2017 at 19:33
  • @chepner not exactly. I can't call source file from the terminal and expect the same results. Because that never worked. The only time I get the same results is when I create a sub shell explicitly, whether via the terminal, or from the script. I can however use bash instead of source to execute the script, and get the same results, but only when I execute my script in a sub shell explicitly
    – user243760
    CommentedJul 27, 2017 at 21:21
  • @chepner by install stuff, I actually mean create a virtual environment, activate that virtual environment, and then execute pip install -r $apath/requirements.txtwithin that activated environment. It's why I used source in the first place to call the script
    – user243760
    CommentedJul 27, 2017 at 21:24
3

Instead of set -o errexit, you can use trap:

trap 'trap - ERR RETURN; kill -INT $$ ; return' ERR RETURN 

kill -INT $$ acts like exit, but if the script was sourced it does nothing (and the return runs instead) (I believe this works because the input prompt catches the signal, preventing the shell from exiting)

trap - ... disables the traps so they don't run after the script exits

    1

    As a simple workaround, you could run a shell in your current shell and source there. Something like:

    1. Open a new terminal and set everything up the way you want it. You mentioned some environment variables and the like. Set them up here.

    2. In that terminal, start a new shell. For instance, bash.

    3. Do your thing. Source your script. If it exits, you're just thrown into the first shell and everything is still set up. Just run bash again and you're back in business.

    To illustrate, I created this script which will fail if you try to source it:

    $ cat /home/terdon/scripts/bar.sh set -o errexit var='bar 

    Let's see what happens if I start a nested shell session and then source it (note that I am using the portable name for the source command, .; source is a bashism):

    parent-shell $ bash ## start a new shell child-shell $ . ~/scripts/bar.sh bash: /home/terdon/scripts/bar.sh: line 2: unexpected EOF while looking for matching `'' parent-shell $ 

    As you can see, the syntax error caused the sourced script to exit which, in turn caused my shell session to exit but since it was a nested session, that just landed me back at the original, parent shell with all the variables still set up. Now, just run a new shell again and you can go back to sourcing your script.

    5
    • I'm going to try this really quick
      – user243760
      CommentedJul 27, 2017 at 18:16
    • This has the intended effect... the downside is that the variables I create for the parent shell, aren't accessible in the child shell, which contains one of the variables I need for execution in the child shell. Is there a way to spawn a subshell from the file I'm executing? That way, I can still call the script in the parent shell, and can still access the variables I need to? I'm literally setting create_path=~/Desktop/site_builder/create.sh in the parent shell because I do it so often. And I need it to call source $create_path [argument] to execute the script.
      – user243760
      CommentedJul 27, 2017 at 18:24
    • 1
      @JillRussek if the variables are not being passed to the child shell, you're not exporting them. But what you describe really makes very little sense. It is sounding more and more like an XY problem. You might want to post a new question explaining what your end objective is. I bet we can give you a better solution that all this weird sourcing.
      – terdon
      CommentedJul 27, 2017 at 18:54
    • Hmm. I might give that a shot too. Right now, just spawning the child shell from the script instead of from the terminal is doing exactly what I want it to. I have to source the script from the terminal in order to create a virtual environment in the script, and pip install stuff in it. It's working as intended now.
      – user243760
      CommentedJul 27, 2017 at 19:02
    • But you're right, I wasn't exporting the variables, because I didn't know I needed to :D. (I've never spawned a child shell before)
      – user243760
      CommentedJul 27, 2017 at 19:11

    You must log in to answer this question.