in reply to There has got to be a Cleaner Way!

Not knowing what an 'RCS' is or does, and if, as I suspect, this is intended to run as a cron job, adding one new user at a time, then if I were tackling this problem I might set out to do it like this.

Pseudo code.

  1. Open the file rw.
  2. Read until I find the line that starts with the domain of interest.
  3. Read on until I found a line that does not contain the domain of interest remembering position where each line started till I find it.
  4. Once I found it, I know where that line started, append the rest of the file onto the end of the string that has it in.
  5. Rewind to the saved position.
  6. Write the new line.
  7. Re-write the rest.
  8. Close and done.

Perl code

my $datafile = '/etc/postfix/virtual'; =pod # This bits a mess? # First you composite the username and domain together my $req_addr = "$username\@$domain"; # Then you use a very iffy regex to break them apart? my ( $name, $domain ) = $req_addr =~ /^([\w.-]+)(@[\w.-]+)$/ ; =cut # so assuming that $name and $domain contain the right information my ($line, $mark, $rest); open FH, "+<$datafile" or die "Open $main::FH failed; $!\n"; #1 $line = <FH> while $line !~ /^$domain$/o; #2 $mark = tell(FH), $line = <FH> while $line =~ /$domain/o; #3 @rest = ($line, <FH>); #4 truncate( FH, $pos ); #5 print FH "$name\@$domain\t$name\n"; #6 print FH "@rest"; #7 close FH or die warn "Error closing $main::FH; $!\n"; #8

Warning! However, if anything should go wrong whilst your processing, your datafile could end up corrupted.

A better way of doing this would be to use the -spi switches from perlman:perlrun

Something like this

#! perl -wspi.bak BEGIN{ die <<USAGE unless defined($domain) & $domain ne '1' && defined($ +name) & $name ne '1'; Usage: $0 -spi -name=fred --domain=some.com path/to/userfile USAGE my ($started, $finished) = (0,0); } next if $finished; # Just print the rest once +we've done the biz $started = /^$domain/o # look for the start of the + right block or next unless $started; # and just print till we f +ind it $finished = !/$domain/o # Continue just printing ti +ll we find the end or next unless $finished; print "$name\@$domain\t$name\n"; # Then print the new bit END{ unlink $main::ARGV . $^I or warn "Failed to delete backup $main::ARGV$^I; $!\n" }

Which works quite nicely on my system using your sample file format.

The caveat with this (and the preceding and yours) is that if the same user registers several times--as users are apt to do if they don't see immediate results--you will end up with multiple lines for that user.

Fixing this limitation is left AAEFTP.


Well It's better than the Abottoire, but Yorkshire!

Replies are listed 'Best First'.
Re: Re: There has got to be a Cleaner Way!
by jimbobn (Novice) on Sep 01, 2002 at 06:53 UTC
    Thanks for that. I already have the 'multiple instance of same user' thing sorted.
    Because the accounts rely on a unix username, i run the adduser command before making any other mod's, and if it returned that the user already exists, then i break from the code and send the user an e-mail to tell them that the account is taken.