1

How is it possible that operator >> and stream redirection operator are passed to the function try() which catches errors and exits...

When I do this :

exitFunc() { echo "EXIIIIIIIIIIIIIIIIT" }

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exitFunc 111; }
try() { "$@" || die "cannot $*"; }

try commandWhichFails >> "logFile.log" 2>&1

When I run the above, also the exitFunction echo is output into the logFile... How do I need to change the above that the try command does basically this

try ( what ever comes here >> "logFile.log" 2>&1 ) 

Can this be achieved with subshells?

Gabriel
  • 8,990
  • 6
  • 57
  • 101

2 Answers2

3

If you want to use stderr in yell and not have it lost by your redirection in the body of the script, then you need to preserve it at the start of the script. For example in file descriptor 5:

#!/bin/bash
exec 5>&2
yell() { echo "$0: $*" >&5; }
...

If your bash supports it you can ask it to allocate the new file descriptor for you using a new syntax:

#!/bin/bash
exec {newfd}>&2
yell() { echo "$0: $*" >&$newfd; }
...

If you need to you can close the new fd with exec {newfd}>&-.

meuh
  • 11,500
  • 2
  • 29
  • 45
  • @Qualia you are right. I remembered seeing a new way of duplicating fds, and added it to my answer. – meuh Aug 16 '15 at 15:18
0

If I understand you correctly, you can't achieve it with subshells.

If you want the output of commandWhichFails to be sent to logFile.log, but not the errors from try() etc., the problem with your code is that redirections are resolved before command execution, in order of appearance.

Where you've put

try false >> "logFile.log" 2>&1

(using false as a command which fails), the redirections apply to the output of try, not to its arguments (at this point, there is no way to know that try executes its arguments as a command).

There may be a better way to do this, but my instinct is to add a catch function, thus:

last_command=
exitFunc() { echo "EXIIIIIIIIIIIIIIIIT"; } #added ; here

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exitFunc 111; }
try() { last_command="$@"; "$@"; }
catch() { [ $? -eq 0 ] || die "cannot $last_command"; }

try false >> "logFile.log" 2>&1
catch

Depending on portability requirements, you can always replace last_command with a function like last_command() { history | tail -2 | sed -n '1s/^ *[0-9] *//p' ;} (bash), which requires set -o history and removes the necessity of the try() function. You can replace the -2 with -"$1" to get the N th previous command.

For a more complete discussion, see BASH: echoing the last command run . I'd also recommend looking at trap for general error handling.

Community
  • 1
  • 1
Qualia
  • 700
  • 8
  • 15