Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I'm used to this style in other languages:

do_something || (log_error; return 1) # do something, and if that fails, log the error and then return 1 no matter what, even if the logging fails.

But I can't seem to find an equivalent in bash. The problem is that the parenthesis work kind of like a function with its own scope, and the return 1 won't have the expected behavior.

This is what I have so far, but it's not perfect:

! do_something && log_error && return 1

The problem with this is that the ! is confusing, and the return 1 depends on the success of the logging.

This one is better, but more verbose:

do_something || (log_error; return 1) || return 1

Any thoughts?

share|improve this question
    
The return statement is meaningful only when inside a script or function - not on the command line. Who do you want to return 1 to? –  Herman Torjussen Jan 12 '13 at 16:29
    
@htor You are right, I always use this snippet inside a function. Because sometimes I need to include the script, and some other times I need to execute it as a standalone script. –  ChocoDeveloper Jan 12 '13 at 17:18
    
@ChocoDeveloper: Use return to return from a shell function but continue executing the script from where it was called; use exit to exit the entire shell (i.e. the script). Unless you're in parentheses, in which case you're in a subshell and exit only exits the parenthesized (subshell) expression. –  Gordon Davisson Jan 12 '13 at 18:16
    
@GordonDavisson Yes, the problem is I have the payload of the script inside parenthesis (outside I only have some vars and functions I always use), to redirect the stdout/stderr (most times I want both) to a log file, and exit stopped that redirection last time I tried. That way I can have each script dealing with its own logs, instead of doing that from the caller (eg, from the cli, or a cron file). –  ChocoDeveloper Jan 12 '13 at 20:51
1  
@ChocoDeveloper: Since each script (normally) runs as a separate shell, you can redirect with e.g. exec >output.log 2>error.log and it won't mess up the calling script. Or you can use { payload; } >output.log 2>error.log and the redirect will only apply to what's in the brackets, without needing a subshell. –  Gordon Davisson Jan 12 '13 at 23:37
show 1 more comment

2 Answers

up vote 4 down vote accepted

Use braces.

 do_something || { log_error; return 1;}
share|improve this answer
    
Needs more spaces. –  Ignacio Vazquez-Abrams Jan 12 '13 at 17:12
    
@IgnacioVazquez-Abrams: thanks; fixed. –  BatchyX Jan 12 '13 at 17:18
    
Perfect, thanks! –  ChocoDeveloper Jan 12 '13 at 20:58
add comment

The exit status of the last command run is saved in the $? variable. So, you could do something like the following:

do something
if [[ $? > 0 ]]; then 
   do log_error; exit 1; 
fi

I like that for readability's sake. However, yours is perfectly good, you just need to replace && with ;:

! do_something && log_error ; return 1

&& means "execute the next command ONLY IF the previous one exited successfully" while ; just means "execute the next command".

share|improve this answer
    
In your second example, return 1 is executed in case of success. –  BatchyX Jan 12 '13 at 16:38
    
As far as I can tell, it is executed in case of both success and failure as desired. The exit status of ! ls /etc && echo "FAIL" > ~/log ; return 1 AND of ls /etcaaa && echo "FAIL" > ~/log ; return 1 is 1. –  terdon Jan 12 '13 at 16:46
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.