An shell script idiom for choosing what file to operate on

May 30, 2006

This is one of those Bourne shell idioms where I show the code first and explain the effect later:

#!/bin/sh
def=$1; shift
case "$#" in
  0) if test -t 0; then
        cat -- $def
     else
         cat
     fi;;
  *) cat -- "$@";;
esac

Call this selfile. You use it in things like log scanning scripts, invoking it as:

selfile /var/log/something "$@" | ... scan stuff ...

The selfile script picks what to feed the rest of the script in the following way:

  1. if you supplied any arguments, it takes them as filenames.
  2. if you're invoking it interactively, it uses a default file.
  3. if you're feeding something to its standard input, it just passes it on, so you can do things like 'zcat /var/log/Old/foo.* | scanner'.

These three choices turn out to be pretty much what you want in practice. You don't have to give any arguments if you want to scan today's logs, while you still have the option of feeding it stuff to digest.

The magic is 'test -t 0', which is true only if standard input is a terminal (real or pseudo); this is our somewhat crude way of telling whether we're 'interactive' or non-interactive.

(Disclaimer: while you can use this trick on standard output to have your script behave differently when it's in a pipe or otherwise having its standard output redirected, you probably don't want to do that. People get twitchy when commands produce different output like that, in part because it makes it harder to build up shell scripts bit by bit.)

Written on 30 May 2006.
« An obvious way to do bulk initialization of dictionaries
Fiddling with X selections from shell scripts »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Tue May 30 00:58:43 2006
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.