Updating a Random-Access File (Perl Cookbook, 2nd Edition)

## 8.13. Updating a Random-Access File

### 8.13.1. Problem

You want to read an old record from a binary file, change its values, and write back the record.

### 8.13.2. Solution

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: $!";

### 8.13.3. Discussion

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` is `syswrite`.

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 backward in the file one record, and writes it out.

#### Example 8-4. weekearly

>       #!/usr/bin/perl -w
>       # 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, "+<:raw", "/var/log/lastlog")
>           or die "can't update /var/log/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: $!";

### 8.13.4. See Also

The PerlIO(3) manpage; the `open`, `seek`, `read`, `pack`, and `unpack` functions in the perlfunc(1) and in Chapter 29 of Programming Perl; Recipe 8.12; Recipe 8.14

Copyright © 2003 O'Reilly & Associates. All rights reserved.