2006-05-30
An shell script idiom for choosing what file to operate on
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:
- if you supplied any arguments, it takes them as filenames.
- if you're invoking it interactively, it uses a default file.
- 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.)