On handling logging in a script

This tip isn’t really new, but it is one of those things that I used to know, forgot, remembered, and am now blogging so that it will be easier to find (for me) when I finally forget it again. Besides, I was hanging out with Bex Huff earlier tonight and he told me I need to blog more and this was what popped into my head. On a side note, I would encourage everyone to seek out someone that you “know” from this internet thang when you’re traveling. Meeting in person certainly beats any “8 things” list :). I happen to be in Minneapolis this month, so I looked up Bex and also hope to find a bar or similar establishment where Billy can don the kilt 2.0 for another picture (that may be asking a lot in 31-degree temps, though).

Enough soapbox…on with the tech talk. I’ve had several occasions where I intended to capture all output from commands I put into a shell script, but somehow forgot one or bungled the output redirection of one of them about half way through the script and overwrote the log information (put “>” instead of “>>”). Anyway, this method resolves that and also captures *all* of the output from the commands in the script. The best part is that you don’t have to add all those output redirections to each command. It’s really pretty short syntax, but you have to get it right.

Here’s what you do:

#!/bin/bash
### this one was tested in bash, but should work equally well in other sh-like shells.
LOG=/path/to/logfile.txt
rm -f $LOG ### important to make sure someone didn't symlink it to your db file


### save the file descriptors
exec 3>&1
exec 4>&2


### set up output streams
exec 1> $LOG
exec 2>> $LOG

### do stuff, run commands, whatever
### all the commands run here will be logged to the $LOG file

### restore the file descriptors
exec 1>&3
exec 2>&4
### commands that come here will not be logged to the $LOG

That’s it, pretty short and sweet. Hope it helps you–I know it makes for much prettier scripts and generally makes for more helpful logfiles too. If any of the scripting gurus have suggestions for improvements, please comment below or drop me an email.

  • http://www.preferisco.blogspot.com Nigel Thomas

    Dan

    Another approach (which also works on Windows) is to place the commands you want to log into a bracketed section

    (
    commands to be logged
    more commands to be logged
    ) > $LOG

    This has the added benefit on *nix that you can easily tee and grep the output. So you can log *everything* to a file, but filter out (say) significant progress and error messages. I tend to start my messages with == (the more equals signs, the more important) and then you can filter messages by how many == they have:

    (
    commands
    ) | tee $log | grep “^===”

    Of course on *nix you can also redirect stderr at the same time in the usual way(s) eg:

    (
    commands
    ) >2&1 >$LOG

    I think using brackets (braces) like this is easier to read – and you can nest the brackets too. Just make sure you match brackets properly.

    Regards Nigel

  • http://www.dannorris.com/ Dan

    That’s a nice way to handle it. I don’t agree that it makes the script code easier to read with lots of parens hanging around, but I still think it’s a useful technique–I like the filtering too.

    Thanks for the nice tip!

  • http://blogs.oracle.com/fusionecm billy

    no kilt no way. too cold, the boyz will freeze.