Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Updating a conf file non-destructively

by rlb3 (Deacon)
on Jul 11, 2006 at 22:05 UTC ( [id://560560]=perlquestion: print w/replies, xml ) Need Help??

rlb3 has asked for the wisdom of the Perl Monks concerning the following question:

I'm try to edit a FreeBSD rc.conf file. The data part of the file is made of key value pairs like:

sshd_enable="YES"

There can also be comments and empty lines. What I need to be able to do is update values from "YES" to "NO" or add new key/value pairs to the end of the file without disturbing the comments or the empty lines. The problem with my code is that it duplicates the file in my new file and I'm not sure why. This is what I have so far and a test file:

package Init::Freebsd::RCConf::Line; use strict; use warnings; use Class::Std; { my %raw_line : ATTR( :get<raw_line> :set<raw_line> ); my %key : ATTR( :get<key> :set<key> ); my %value : ATTR( :get<value> :set<value> ); sub BUILD { my ( $self, $ident, $args_ref ) = @_; $raw_line{$ident} = $args_ref->{'line'} || ''; $self->parse(); } sub parse { my $self = shift; if ( $self->get_raw_line() =~ /=/) { my ( $key, $value ) = split( /=/, $self->get_raw_line(), 2 + ); $self->set_key($key); $self->set_value($value); } else { $self->set_key(''); $self->set_value( $self->get_raw_line() ); } } sub print_line { my $self = shift; if ( $self->get_key() ne '' ) { return sprintf( "%s=%s", $self->get_key(), $self->get_valu +e() ); } else { return $self->get_value(); } } } 1;
package Init::Freebsd::RCConf; use strict; use warnings; use Carp; use Fcntl qw(:flock); use FindBin; use Init::Freebsd::RCConf::Line; use File::Copy; use Class::Std; { my %file : ATTR( :get<file> :set<file> ); my %file_name : ATTR( :get<file_name> :set<file_name> ); sub BUILD { my ( $self, $ident, $args_ref ) = @_; $file_name{$ident} = (-f '/etc/rc.conf') ? '/etc/rc.conf' : $F +indBin::Bin . '/rc.conf'; $file{$ident} = []; $self->load(); } sub add_line : RESTRICTED { my $self = shift; my $line = shift; my $ident = ident($self); push @{ $file{$ident} }, $line; } sub load { my $self = shift; my $ident = ident($self); open my $read_fh, '<', $self->get_file_name() or croak('Can no +t find ' . $self->get_file_name() . ': ' . $!); flock($read_fh, LOCK_SH); while ( <$read_fh> ) { chomp; #print $_ . "\n"; my $line = Init::Freebsd::RCConf::Line->new( { line => $_ +} ); $self->add_line($line); } close($read_fh); } sub set_value { my $self = shift; my $key = shift; my $value = shift; my $ident = ident($self); $key .= '_enable'; $value = sprintf(qq{"%s"}, $value); for my $line (@{ $file{$ident} }) { if ( $line->get_key() eq $key ) { $line->set_value($value); return; } } # If we've made it this far, it must be new line and # will be added to the end of the file my $new_line = Init::Freebsd::RCConf::Line->new(); $new_line->set_key($key); $new_line->set_value($value); $self->add_line($new_line); } sub write_file { my $self = shift; my $ident = ident($self); my $temp_file = '/tmp/temp.rc.conf'; open my $write_fh, '>', $temp_file or die 'Cannot open '. $tem +p_file . ': ' . $!; flock($write_fh, LOCK_EX); for my $line ( @{ $file{$ident} } ) { print {$write_fh} $line->print_line() . "\n"; } close($write_fh); File::Copy::copy($temp_file, $self->get_file_name()) or croak +'Unable to copy file ' . "$!"; unlink $temp_file if (-s $self->get_file_name() ); } } 1;
#!perl use Test::More tests => 8; use FindBin; use Init::Freebsd::RCConf; my @methods = qw{ load add_line set_value write_file }; my $file_name = $FindBin::Bin . '/rc.conf2'; build_conf($file_name); my $conf = Init::Freebsd::RCConf->new(); $conf->set_file_name($file_name); isa_ok( $conf, 'Init::Freebsd::RCConf' ); can_ok( $conf, $_ ) for (@methods); $conf->load(); $conf->set_value( 'sshd', 'NO' ); $conf->write_file(); my $sshd = look_for( $file_name, 'sshd' ); ok( $sshd =~ /NO/, 'SSH set to "NO"' ); $conf->set_value( 'ssl-something', 'YES' ); $conf->write_file(); my $ssl = look_for( $file_name, 'ssl-something' ); ok( $ssl =~ /YES/, 'ssl-something set to YES' ); $conf->set_value( 'ssl-something', 'NO' ); $conf->write_file(); my $ssl = look_for( $file_name, 'ssl-something' ); ok( $ssl =~ /NO/, 'ssl-something set to NO' ); unlink($file_name); ###################################################################### +######### sub build_conf { my $file_name = shift; open( my $file, '>', $file_name ) or die 'Cannot open file' . "\n" +; for my $line (<DATA>) { print {$file} $line; } close $file; } sub look_for { my $file_name = shift; my $service_name = shift; open( my $file, '<', $file_name ) or die 'Cannot open file' . "\n" +; for my $line (<$file>) { return $line if $line =~ /$service_name/; } } __DATA__ #This is a example file. sshd_enable="YES" named_enable="YES" pureftpd_enable="YES" mysql_enable="YES"

Thanks for any help.

Replies are listed 'Best First'.
Re: Updating a conf file non-destructively
by Fletch (Bishop) on Jul 11, 2006 at 23:25 UTC

    Your blob of code aside, don't underestimate the one-liner.

    perl -i -pe 's/^sshd_enable=".*?"/sshd_enable="YES"/' /etc/rc.conf

    Additionally: If you really want you can even get fancy:

    perl -i -pe 's/^sshd_enable="(.*?)"/qq{sshd_enable="} . ( $1 eq "YES" +? "NO" : "YES" ) . qq{"}/e' /etc/rc.conf
Re: Updating a conf file non-destructively
by shmem (Chancellor) on Jul 11, 2006 at 23:05 UTC
    The problem with my code is that it duplicates the file in my new file and I'm not sure why.

    You read the file twice. Once in sub BUILD in Init::Freebsd::RCConf and once in the test script ($self->load() and $conf->load()).

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Updating a conf file non-destructively
by kwaping (Priest) on Jul 11, 2006 at 22:59 UTC
    I haven't read your entire block of code yet, but I do have one piece of advice already - explore opening your file using the append mode ('>>') instead. This will address your concern about adding "new key/value pairs to the end of the file without disturbing the comments or the empty lines".

    ---
    It's all fine and dandy until someone has to look at the code.
Re: Updating a conf file non-destructively
by mickeyn (Priest) on Jul 12, 2006 at 05:03 UTC
    Tie your file to an array and easily update/append what you want.

    Tie::File is what you need.

    Enjoy,
    Mickey

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://560560]
Approved by Hue-Bond
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-04-26 04:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found