3

I have a need to group commands using curly braces in my shell script so that I can direct their output to separate log files like so ...

    >cat how-to-exit-script-from-within-curly-braces.sh 

{
  printf "%d\n" 1
  printf "%d\n" 2
} | tee a.log
{
  printf "%d\n" 3
  printf "%d\n" 4
} | tee b.log

    >./how-to-exit-script-from-within-curly-braces.sh 
1
2
3
4
    >cat a.log 
1
2
    >cat b.log 
3
4
    >

Although I have added the curly braced to facilitate logging, I would still like the script to exit when the exit command is invoked inside the curly braces.

It does not do so of course. It only exits the curly braces and then continues on executing the remainder of the script like so ...

    >cat how-to-exit-script-from-within-curly-braces.sh 

{
  printf "%d\n" 1
  exit
  printf "%d\n" 2
} | tee a.log
{
  printf "%d\n" 3
  printf "%d\n" 4
} | tee b.log

    >./how-to-exit-script-from-within-curly-braces.sh 
1
3
4
    >cat a.log 
1
    >cat b.log 
3
4
    >

making the exit code non-zero and adding "set -e" to the script does not appear to work ...

    >cat how-to-exit-script-from-within-curly-braces.sh 
set -e

{
  printf "%d\n" 1
  exit 1
  printf "%d\n" 2
} | tee a.log
{
  printf "%d\n" 3
  printf "%d\n" 4
} | tee b.log

    >./how-to-exit-script-from-within-curly-braces.sh 
1
3
4
    >cat a.log 
1
    >cat b.log 
3
4
    >

Is there a way to force the exit of the script from within the curly braces?

Alex Ryan
  • 3,719
  • 5
  • 25
  • 41
  • 1
    Good Q except for the smileys (I'm feeling grumpy;->) but wasn't sure if S.O. was now converting some mistaken shell syntax of yours to a smiley. Good luck to all! – shellter Jan 12 '17 at 04:31

1 Answers1

5

There is no problem with exit and curly braces:

{
  exit
}
echo "This will never run."

However, there is a problem with exit and pipes, and that's what you're running into:

exit | exit
echo "Still alive"

In bash by default, each stage in a pipeline runs in a subshell, and exit can only exit that subshell. In your case, you can use redirection and process substitution instead:

{
  printf "%d\n" 1
  exit 1
  printf "%d\n" 2
} > >(tee a.log)
echo "This will not run"

Note that this is bash specific code, and will not work in sh (such as when using #!/bin/sh or sh myscript). You have to use bash instead.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • that worked beautifully. very informative. thank you – Alex Ryan Jan 12 '17 at 00:34
  • 1
    FYI ... "set -o pipefail" seems to work as well. "used to propagate errors so that the return value of the pipeline command is non-zero if one of the preceding commands exited with a non-zero status" http://stackoverflow.com/a/19622300/2341218 – Alex Ryan Jan 12 '17 at 02:02