Hi.
A GNU/Linux package, moreutils, contains a few interesting utilities. Of relevance here is sponge. It sucks up data from STDIN, and spits it out to a file of your choice [default STDOUT] at EOF. This alleviates the common error of re-directing STDOUT to the original input file. It's a good general solution, but it does involve using a temporary file -- we often accept a little overhead for convenience.
Here's a demonstration driver script using cat and sed:
#!/usr/bin/env bash
# @(#) s1 Demonstrate sponge.
echo
set +o nounset
LC_ALL=C ; LANG=C ; export LC_ALL LANG
echo "Environment: LC_ALL = $LC_ALL, LANG = $LANG"
echo "(Versions displayed with local utility \"version\")"
version >/dev/null 2>&1 && version "=o" $(_eat $0 $1) perl
set -o nounset
echo
FILE=${1-data1}
SPONGE=./sponge
cp original-data $FILE
echo " Data file $FILE:"
cat $FILE
echo
echo " Results for default STDOUT:"
cat $FILE | $SPONGE
cp original-data $FILE
echo
echo " Results for re-writing $FILE:"
sed '/2/d' $FILE | $SPONGE $FILE
cat $FILE
exit 0
Producing:
% ./s1
Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 2.6.11-x1, i686
Distribution : Xandros Desktop 3.0.3 Business
GNU bash 2.05b.0
perl 5.8.4
Data file data1:
1
2
3
Results for default STDOUT:
1
2
3
Results for re-writing data1:
1
3
Not everyone wants to obtain GNU/Linux utilities, so I hacked together a perl script to do the basic job. No warranties, etc.:
#!/usr/bin/perl
# @(#) sponge Read STDIN, write to file at EOF.
## Modification history: when / who / what: most recent at top.
# Relocate to end if grows too long, or re-sequence.
#
# 2009.04.06 / drl / original.
use warnings;
use strict;
my ($debug);
$debug = 1;
$debug = 0;
my ( $buf, $file, $tmp, $temporary );
$file = shift || ">-";
$temporary = "/tmp/sponge_$$";
open( $tmp, ">", $temporary ) || die " Cannot open $temporary for write.\n";
print " debug write - temporary is :$temporary:\n" if $debug;
while ( read( STDIN, $buf, 16384 ) ) {
print $tmp $buf;
}
close $tmp;
open( $tmp, "<", $temporary ) || die " Cannot open $temporary for read.\n";
print " debug read - temporary is :$temporary:\n" if $debug;
if ( $file eq "-" ) {
open( FILE, ">-" ) || die " Cannot open STDOUT for writing.\n" if $debug;
}
else {
open( FILE, ">$file" ) || die " Cannot open $file for write.\n";
}
print " debug write - file is :$file:\n" if $debug;
while ( read( $tmp, $buf, 16384 ) ) {
print FILE $buf;
}
unlink $temporary;
exit(0);
So sponge can be used for any such situation, sed, awk, grep, etc. To make effective use of sponge, you'd probably want to place it in a directory in your path, say, $HOME/bin for most users. That would eliminate the requirement of needing it in the current directory.
Kernighan and Pike addressed this issue in a slightly different way in 1978 with a script called overwrite in The UNIX Programming Environment, page 152 ff.
One possible useful modification would be to omit the temporary file if the data i short enough -- left as an exercise for the reader ... cheers, drl