[ILUG] Logging and displaying output of shell commands

Brian Foster blf at utvinternet.ie
Tue Oct 7 18:29:29 IST 2008


  | Date: Tue, 7 Oct 2008 12:03:38 +0100
  | From: Darragh Bailey <felix at compsoc.nuigalway.ie>
  |[ ... ]
  | make macro
  | log = $(if $(filter 2,$(V)),2>&1 | tee -a $1,>> $1 2>&1)
  | 
  | Problem occurs that if V=2, tee is used which results in the wrong exit
  | code being received by the make rule.
  | 
  | i.e.
  | rule:
  | 
  | somerule:
  | 		@some_command $(call log,mylog.log)
  | 
  | With V=1, this becomes:
  | somerule:
  | 		@some_command >> mylog.log 2>&1
  | 
  | With V=2, this becomes:
  | somerule:
  | 		@some_command 2>&1 | tee -a mylog.log
  | 
  | Problem is that when there is an error executing some_command and V=2,
  | then make only receives the exit code of tee, which barring some problem
  | in tee itself is always going to be 0.
  | 
  | Any suggestions on how to get the output to go to the log file in all
  | three cases while still resulting in the correct exit code being given
  | to make?

 well, I'd tell the user to run:

    make V=1 RULE  2>&1  |  tee -a FILE

 but I do understand people don't do that when they should.
 (there's numerous variations on the idea, such as using
 script(1) or other session-logging commands.)

 so, continuing with yer basic approach, and assuming the
 underlying shell is a fairly modern version of GNU bash(1),
 then try changing the rule's expansion from:

    some_command  2>&1 |  tee -a mylog.log
 to:
    set -o pipefail;  some_command  2>&1 |  tee -a mylog.log

 from the bash(1) 3.2 man page:

    If [`pipefail' is] set, the return value of a
    pipeline is the value of the last (rightmost)
    command to exit with a non-zero status, or zero
    if all commands in the pipeline exit successfully.
    This option is disabled by default.

 an older trick, albeit still GNU `bash'-only, is
 (I'm omitting all the spurious backslashes (\),
 semicolons (;), &tc used in make(1)'s baroque syntax):

    some_command  2>&1 |  tee -a mylog.log
    for sts in ${PIPESTATUS[*]}; do
	[ $sts -eq 0 ] || exit $sts
    done

 WARNING: there must NOT be any commands between the pipe
 and the `for'-loop.  every command changes `PIPESTATUS'.

 the oldest(?) trick of all, which works in most(? all?)
 Bourne-ish shells (excepting the obvious trick of using
 a temporary file instead of piping to tee(1)) is:

    ( some_command; echo $? >ERR )  2>&1 |  tee -a mylog.log
    read sts <ERR && [ "$sts" -eq 0 ] || exit 1

 (there's numerous variations on using a temporary file
 to communicate the exit status, such as using a FIFO.)

 hope this helps.  (all of the examples above were just
 typed in without testing.  apologies for any mistakes.)

cheers!
	-blf-

-- 
“How many surrealists does it take to    |  Brian Foster
 change a lightbulb?  Three.  One calms  |  somewhere in south of France
 the warthog, and two fill the bathtub   |     Stop E$$o (ExxonMobil)!
 with brightly-coloured machine tools.”  |       http://www.stopesso.com



More information about the ILUG mailing list