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

I'm a bit new to perl scripting, so I would like to ask for some advice. I have tried several variations of scripts I've found on the net, but can't seem to get them to work out just right.

I have a file with the following information...

# Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; fixed-address 10.100.34.117; hardware ethernet 11:42:a3:FD:78:C3; } # Host 3 host 3A684 { filename "junk6.cm"; fixed-address 10.100.34.119; hardware ethernet 11:42:a3:13:a6:84; } # Host 4 host 46d54 { filename "junk4.cm"; fixed-address 10.100.34.120; hardware ethernet 23:10:3d:14:6d:54; }
I have another script that is rearranging this just fine with one exception. The lines that have 'fixed-address' as the last line before the close bracket are working fine. The lines that have 'hardware ethernet' on them are causing my big script to misinterpret the information.

My desired result would be the following...

# Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; fixed-address 10.100.34.117; hardware ethernet 11:42:a3:FD:78:C3; } # Host 3 host 3A684 { filename "junk6.cm"; fixed-address 10.100.34.119; hardware ethernet 11:42:a3:13:a6:84; } # Host 4 host 46d54 { filename "junk4.cm"; fixed-address 10.100.34.120; hardware ethernet 23:10:3d:14:6d:54; }
So what I'd like to build is a script that would search for 'fixed-address'. If it's followed by '}', do nothing. If it's followed by 'hardware ethernet', flip the lines 'fixed-address' and 'hardware ethernet'.

Does anyone have any advice on how they think that they would accomplish this? I'd appreciate any help that someone could provide.

Replies are listed 'Best First'.
Re: swapping lines that match a condition
by SuicideJunkie (Vicar) on Sep 10, 2009 at 14:15 UTC

    Sounds like a job for regular expressions:

    $stuff =~ s/(frontStuff)(stuffToMove1)(middleStuff)(stuffToSwap2)(endi +ngStuff)/$1$4$3$2$5/;
    Grab the chunks and swap the #2 and 4 captures for the replace.

    PS: This would require multi-line match mode, given your input data; the "middlestuff" would be a newline.


    An alternative way to do it, given the regularity of your data, might be to save each record into a hash, then print the hash values back out in whichever order you like. Useful if you're not totally sure of your data, and could be used to remove duplicates or fill in missing fields quite easily.

      Thanks SJ!

      I need to walk through this a little so that I can educate myself and understand.

      So if I'm understanding what you are saying, my expression would look something like this:

      s/(^#)(^host)(^filename)(^fixed)(^hardware)(^})/$1$2$3$5$4$6/;

        Close, but you need the /m modifier (See m). This makes ^ match the beginning of the line and $ match the end of the line anywhere within a single string. Note that you'll have to have the whole file as a string to use this. Also, you'll need to match the whole of each line. So a solution might look like this:

        #!/usr/bin/perl use strict; use warnings; my $filename = shift or die "Need a filename"; my $text; SLURP:{ local $/; open my $fh, '<', $filename or die "Error opening $filename: $!"; $text = <$fh>; close $fh; } $text =~ s/(^fixed.*) #match fixed-address \n #followed by newline (^hardware.*) #followed by hardware / $2\n$1 #and switch them (remember newline!) /mgx; #m for multiline, g for multiple matches print $text;
        print pack("A25",pack("V*",map{1919242272+$_}(34481450,-49737472,6228,0,-285028276,6979,-1380265972)))
Re: swapping lines that match a condition
by juster (Friar) on Sep 10, 2009 at 15:47 UTC
    The lines that have 'hardware ethernet' on them are causing my big script to misinterpret the information.

    A more elegant solution might be to fix the "big script" not to care about the order the lines come in. Here is one example that parses the config file and returns hash references of all the data.

    #!/usr/bin/perl use warnings; use strict; sub parse_host_cfg { my ($file) = @_; my %fields; HOST_CFG_LOOP: while ( my $line = <$file> ) { chomp $line; last HOST_CFG_LOOP if ( $line =~ /\A\s*}/ ); if ( my ($name, $value) = $line =~ /^([-\w\s]+) "?(.*?)"?;/ ) +{ $fields{$name} = $value; } else { warn "invalid host config line: $line"; } } return \%fields; } sub parse_cfg_file { my %hosts; my ($file) = @_; while ( my $line = <$file> ) { chomp $line; if ( my ($hostid) = $line =~ /^host\s+(\w+)\s+{/ ) { my $host_cfg = parse_host_cfg( $file ); $hosts{$hostid} = $host_cfg; } } return \%hosts; } use Data::Dumper; print Dumper parse_cfg_file( *DATA ); __DATA__ # Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; fixed-address 10.100.34.117; hardware ethernet 11:42:a3:FD:78:C3; aklsjdhflkajsdfhlkjh } } # Host 3 host 3A684 { filename "junk6.cm"; fixed-address 10.100.34.119; hardware ethernet 11:42:a3:13:a6:84; } # Host 4 host 46d54 { filename "junk4.cm"; fixed-address 10.100.34.120; hardware ethernet 23:10:3d:14:6d:54; }

      Thanks Guys! I think that I will be able to use these suggestions.

      I appreciate all the help that you are able to give a newb like myself!

Re: swapping lines that match a condition
by ack (Deacon) on Sep 10, 2009 at 15:56 UTC

    SuicideJunkie's approach is very compact. An alternative approach (not nearly as compact or elegant, but perhaps more straightforward) still uses regular expressions (Regex's) but does the swapping in a somewhat more pedestrian fashion is shown below:

    Given the OP's input, the output looks like the following:

    # Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; hardware ethernet 11:42:a3:FD:78:C3; fixed-address 10.100.34.117; } # Host 3 host 3A684 { filename "junk6.cm"; hardware ethernet 11:42:a3:13:a6:84; fixed-address 10.100.34.119; } # Host 4 host 46d54 { filename "junk4.cm"; hardware ethernet 23:10:3d:14:6d:54; fixed-address 10.100.34.120; }

    I hope this helps. I tried to comment the code so that the OP can see the steps and the logic in a way that someone who is not too familiar with Perl can follow. I may have misjudged the OP's level of familiarity with Perl; if I have, I appologize.

    ack Albuquerque, NM
Re: swapping lines that match a condition
by jwkrahn (Abbot) on Sep 10, 2009 at 16:32 UTC
    $ echo '# Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; fixed-address 10.100.34.117; hardware ethernet 11:42:a3:FD:78:C3; } # Host 3 host 3A684 { filename "junk6.cm"; fixed-address 10.100.34.119; hardware ethernet 11:42:a3:13:a6:84; } # Host 4 host 46d54 { filename "junk4.cm"; fixed-address 10.100.34.120; hardware ethernet 23:10:3d:14:6d:54; } ' | perl -e' $/ = "}\n"; while ( <> ) { s/(fixed-address.*\n)(hardware ethernet.*\n)/$2$1/; print; } ' # Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; hardware ethernet 11:42:a3:FD:78:C3; fixed-address 10.100.34.117; } # Host 3 host 3A684 { filename "junk6.cm"; hardware ethernet 11:42:a3:13:a6:84; fixed-address 10.100.34.119; } # Host 4 host 46d54 { filename "junk4.cm"; hardware ethernet 23:10:3d:14:6d:54; fixed-address 10.100.34.120; }
Re: swapping lines that match a condition
by ikegami (Patriarch) on Sep 10, 2009 at 17:06 UTC

    Your "desired result" is identical to your original file.

    One solution:

    local $/ = "\n}\n"; while (<>) { s/ ^(fixed-address [^\n]*\n) (.*) ^(hardware ethernet [^\n]*\n) /$3$2$1/xsm; print; }
    Or if the lines are always adjacent when they're in the wrong order,
    local $/ = "\n}\n"; while (<>) { s/ ^ (fixed-address .*\n) (hardware ethernet .*\n) /$2$1/xm; print; }
Re: swapping lines that match a condition
by bichonfrise74 (Vicar) on Sep 10, 2009 at 17:00 UTC
    Perhaps something like this?
    #!/usr/bin/perl use strict; local $/ = '}'; while (my $line = <DATA>) { $line =~ s/(fixed-address\s.*;)\s(hardware ethernet\s.*;)/$2\n$1/; print $line; } __DATA__ # Host 1 host 45583 { filename "junk1.cm"; hardware ethernet 11:42:a3:d4:55:83; fixed-address 10.100.34.114; } # Host 2 host D78C3 { filename "junk5.cm"; fixed-address 10.100.34.117; hardware ethernet 11:42:a3:FD:78:C3; } # Host 3 host 3A684 { filename "junk6.cm"; fixed-address 10.100.34.119; hardware ethernet 11:42:a3:13:a6:84; } # Host 4 host 46d54 { filename "junk4.cm"; fixed-address 10.100.34.120; hardware ethernet 23:10:3d:14:6d:54; }