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

Hi monks I am struck up in a pretty bad problem.I have a file by the name of manager.properties.Now I am writtin g a code which will make an entry into this file.Now the present code(which I am writting below) erases any previous data into the file.Now i want to enhance this code so that if a user enters any data then it is matched with the contents of the file and if its not there then it is appended to the file and if it exists then it should be updated.Help me out please...
sub setManagers() { my $getInputFlag = 1; my $writeFileFlag = 1; my $mCounter = 0; my $ans = &promptUser("Do you wish to configure SNMP Managers +on $var? ", "yes"); if ($ans =~ /^[n]o?/i) { $getInputFlag = 0; $writeFileFlag = 0; } while ($getInputFlag) { my $mNumber = $mCounter+1; my $address; my $valid = 0; my $firstAttempt = 1; while (!$valid) { if ($firstAttempt) { $address = &promptUser("Enter Manager $mNumber IP Address +", "127.0.0.1"); $firstAttempt = 0; } else { $address = &promptUser("ERROR: $address Invalid: Re-enter +Manager $mNumber IP Address ", "127.0.0.1"); } if ($address =~ /^(([3-9]\d?|[01]\d{0,2}|2\d?|2[0-4]\d|25[0- +5])\.){3}([3-9]\d?|[01]\d{0,2}|2\d?|2[0-4]\d|25[0-5])$/) { $valid = 1; } } my $port = &promptUser("Enter Manager $mNumber SNMP Po +rt ", "162"); my $version = &promptUser("Enter Manager $mNumber SNMP + Version ", "2"); my $community = &promptUser("Enter Manager $mNumber +SNMP Community String ", "public"); $managers[$mCounter]{address} = $address; $managers[$mCounter]{port} = $port; $managers[$mCounter]{version} = $version; $managers[$mCounter]{community} = $community; $mCounter++; my $finish = &promptUser("Manager configuration comple +te [y/n]? "); if ($finish =~ /^y(es)?$/i) { $getInputFlag = 0; } } # write manager.properties file $JBOSS_HOME/conf if ($writeFileFlag) { open(FILE, "> $var:$ENV{JBOSS_HOME}/conf/manager.proper +ties"); my $numOfManagers = scalar(@managers); print FILE "manager.total=$numOfManagers\n"; for (my $i=0; $i<$numOfManagers; $i++) { my $mNumber = $i+1; print FILE "\n"; print FILE "# SNMP Manager $mNumber Settings\n +"; print FILE "manager.$mNumber.address=$managers +[$i]{address}\n"; print FILE "manager.$mNumber.port=$managers[$i +]{port}\n"; print FILE "manager.$mNumber.version=$managers +[$i]{version}\n"; print FILE "manager.$mNumber.community=$manage +rs[$i]{community}\n"; print FILE "\n"; } $writeFileFlag = 0; close(FILE); } }

Replies are listed 'Best First'.
Re: updating to a file
by GrandFather (Saint) on Sep 21, 2005 at 10:58 UTC

    You might like to consider this example code using inplace edit:

    use warnings; use strict; # Create test file open testOut, "> test.txt"; print testOut $_ while (<DATA>); close testOut; # Get "user" edits my @userEdits = ( "Manager 1: IP 127.0.0.1" , "Manager 10: IP 127.0.0.10", "Manager 4: IP 127.0.0.40", "Manager 7:", ); # create update hash from user input my %edits; for (@userEdits) { my ($key, $value) = /(\d+):(?:.*?((?:\d{1,3}\.){3}\d{1,3}))?/; $edits{$key} = [$value, 0]; # value and written flag } # Update the file local $^I = '.bak'; local @ARGV = ('test.txt'); my ($key, $value); while (<>) # inplace edit { ($key, $value) = /(\d+):.*?((?:\d{1,3}\.){3}\d{1,3})/; my $line = $_; if (defined $key && exists $edits{$key}) { next if ! defined $edits{$key}->[0]; #delete file entry $line = "Manager $key: IP $edits{$key}->[0]\n"; ++$edits{$key}->[1]; # Mark as written } print $line; } continue { if (eof) { for (sort keys %edits) { next if ! defined $edits{$_} || ! defined $edits{$_}->[0]; print "Manager $_: IP $edits{$_}->[0]\n" if ! $edits{$_}->[1]; } last; } } __DATA__ Manager 1: IP 127.0.0.1 Manager 3: IP 127.0.0.3 Manager 4: IP 127.0.0.21 Manager 7: IP 127.0.0.12

    Perl is Huffman encoded by design.
Re: updating to a file
by Jaap (Curate) on Sep 21, 2005 at 10:50 UTC
    Simply open the file in append mode:
    if (open (my $fh, '>>', "manager.properties")) { print $fh "test\n"; close ($fh); }
      That solves only the overwrite problem. Appending to a file doesn't solve the update problem. For that, you need inplace edit (as GrandFather shows), or via a Tie to the file. But then, a different data structure would be more suitable.

      Paul

Re: updating to a file
by sh1tn (Priest) on Sep 21, 2005 at 11:04 UTC
Re: updating to a file
by blazar (Canon) on Sep 21, 2005 at 13:36 UTC
    Others already gave you good suggestions as to how to do what you want. I feel like adding a few random observations:
    sub setManagers() { my $getInputFlag = 1; my $writeFileFlag = 1; my $mCounter = 0;
    This may be a spurious comment, but in my experience I found that in Perl you seldom need flag variables like that. Not to say that this is "wrong", but sounds suspect...
    my $ans = &promptUser("Do you wish to configure SNMP Managers +on $var? ", "yes");
    In modern perls you most often do not want to adopt the & form of sub call unless you really know what you're doing, which doesn't appear to be the case here...
    if ($ans =~ /^[n]o?/i) {
    [n]?
    if ($address =~ /^(([3-9]\d?|[01]\d{0,2}|2\d?|2[0-4]\d|25[0- +5])\.){3}([3-9]\d?|[01]\d{0,2}|2\d?|2[0-4]\d|25[0-5])$/)
    Here your regex has the form ^(?:/$regex\.){3}/$regex$, so you could increase its readability porting it explicitly into that form. Also, here you have parens for grouping (an alternation), but they're capturing parens, so you may change them into
    (?:[3-9]\d?|[01]\d{0,2}|2\d?|2[0-4]\d|25[0-5])
    instead.

    All in all I'd use an appearently more complex, but IMHO more readable test possibly putting it into a sub. I'd split on /\./\, then check that the return list has four elements, and do a check on each of them. This seems more {portable,maintainable}, especially if -like it seems reasonable- each of them would require a separate range of validity.

    $managers[$mCounter]{address} = $address; $managers[$mCounter]{port} = $port; $managers[$mCounter]{version} = $version; $managers[$mCounter]{community} = $community;
    Also:
    @{ $managers[$mCounter] }[qw/address port version community]= ($address $port $version $community);
    But then I wouldn't probably create the intermediate variables at all, if you ask me...
    my $numOfManagers = scalar(@managers); print FILE "manager.total=$numOfManagers\n"; for (my $i=0; $i<$numOfManagers; $i++) {
    scalar is pleonastic there, and a rule of thumb is that Perl-style for loops should always be preferred over C-style ones, if they're equivalent, i.e. if the latter doesn't buy you something special, which definitely is not the case here. Thus:
    for my $i (0..$#managers) { ... }
    But then chances are that doing some modification in the logic above you may just turn it into
    for my $item (@managers) { ... }