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.


In reply to Updating a conf file non-destructively by rlb3

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.