You want to read in an old record from a binary file, change its values, and write back the record.
After
read
ing the old record,
pack
up the updated values,
seek
to the previous address, and write it back.
use Fcntl; # for SEEK_SET and SEEK_CUR $ADDRESS = $RECSIZE * $RECNO; seek(FH, $ADDRESS, SEEK_SET) or die "Seeking: $!"; read(FH, $BUFFER, $RECSIZE) == $RECSIZE or die "Reading: $!"; @FIELDS = unpack($FORMAT, $BUFFER); # update fields, then $BUFFER = pack($FORMAT, @FIELDS); seek(FH, -$RECSIZE, SEEK_CUR) or die "Seeking: $!"; print FH $BUFFER; close FH or die "Closing: $!";
You don't have to use anything fancier than
print
in Perl to output a record. Remember that the opposite of
read
is not
write
but
print
, although oddly enough, the opposite of
sysread
actually is
syswrite
. (
split
and
join
are opposites, but there's no
speak
to match
listen
, no
resurrect
for
kill
, and no
curse
for
bless
.)
The example program shown in Example 8.4 , weekearly , takes one argument: the user whose record you want to backdate by a week. (Of course, in practice, you wouldn't really want to (nor be able to!) mess with the system accounting files.) This program requires write access to the file to be updated, since it opens the file in update mode. After fetching and altering the record, it packs it up again, skips backwards in the file one record, and writes it out.
#!/usr/bin/perl # weekearly -- set someone's login date back a week use User::pwent; use IO::Seekable; $typedef = 'L A12 A16'; # linux fmt; sunos is "L A8 A16" $sizeof = length(pack($typedef, ())); $user = shift(@ARGV) || $ENV{USER} || $ENV{LOGNAME}; $address = getpwnam($user)->uid * $sizeof; open (LASTLOG, "+</var/log/lastlog") or die "can't update /usr/adm/lastlog: $!"; seek(LASTLOG, $address, SEEK_SET) or die "seek failed: $!"; read(LASTLOG, $buffer, $sizeof) == $sizeof or die "read failed: $!"; ($time, $line, $host) = unpack($typedef, $buffer); $time -= 24 * 7 * 60 * 60; # back-date a week $buffer = pack($typedef, $time, $line, $time); seek(LASTLOG, -$sizeof, SEEK_CUR) # backup one record or die "seek failed: $!"; print LASTLOG $record; close(LASTLOG) or die "close failed: $!";
The
open
,
seek
,
read
,
pack
, and
unpack
functions in the
perlfunc
(1) and in
Chapter 3
of
Programming Perl
;
Recipe 8.12
;
Recipe 8.14