Need help with a question.

T

Trev

I'm having a problem with my Perl script, what I would like the script
to achieve is to read a file, search it for certain words, put the
results into an Array so I can then call each result with $var[1] etc
and print output to a file. I tried doing it without Sub routines but
wasn't able to split the results. When I rapped the code into a Sub I
get these errors:

syntax error at test.pl line 7, near "@cpqlog_data"
syntax error at test.pl line 24, near "}"

How come these errors only appear when I use Sub { } ?

test.pl
use English;
use Warnings;

Sub LoadFile
{
open (DAT, "<output.txt") || die("Could not open file!");
@cpqlog_data=<DAT>;

foreach $cpqlog (@cpqlog_data)
{
{
chomp($cpqlog);

if ($cpqlog =~ /MAC/)
{
$cpqlog =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlog =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlog =~ s/"\/>//i;

}
}
}
close DAT;
}

Sub CreateLOG
{
open (BOO, "<blah2.txt");
@lines=<TMP>;
print $lines[1];
close BOO;
}

LoadFile;
CreateLOG;
 
E

Erwin van Koppen

Trev said:
How come these errors only appear when I use Sub { } ?

For one thing, you should use lowercase: sub { }
open (BOO, "<blah2.txt");
@lines=<TMP>;

You open BOO, but use TMP (which is not opened).

And, you don't check to see of BOO can be opened.

Fix these things, then it should run. However, I doubt it will do what you
want it to do...
 
T

Trev

For one thing, you should use lowercase: sub { }


You open BOO, but use TMP (which is not opened).

And, you don't check to see of BOO can be opened.

Fix these things, then it should run. However, I doubt it will do what you
want it to do...

Thanks, I've made the changes and so far so good.
 
E

Erwin van Koppen

Trev said:
Thanks, I've made the changes and so far so good.

foreach $cpqlog (@cpqlog_data) {
{
chomp($cpqlog);

if ($cpqlog =~ /MAC/) {
$cpqlog =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlog =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlog =~ s/"\/>//i;
}
}
}

You do understand that the above code does not actually change anything in
the file, right? The substitutions in $cpqlog are just discarded at the end
of the loop.

Oh, and you might want to get rid of the superfluous { }'s...
 
T

Trev

foreach $cpqlog (@cpqlog_data) {
    {
    chomp($cpqlog);

    if ($cpqlog =~ /MAC/) {
        $cpqlog =~ s/  <FIELD NAME="Subject" VALUE="//i;
        $cpqlog =~ s/  <FIELD NAME="MAC" VALUE="//i;
        $cpqlog =~ s/"\/>//i;
    }
    }

}

You do understand that the above code does not actually change anything in
the file, right? The substitutions in $cpqlog are just discarded at the end
of the loop.

Oh, and you might want to get rid of the superfluous { }'s...

Yeah, I figured that out, I added a print BOO so the results are
writen to a file which I load in the next sub. This might not be the
best code but I'm still learning.
 
B

Ben Morrow

Quoth Trev said:
I'm having a problem with my Perl script, what I would like the script
to achieve is to read a file, search it for certain words, put the
results into an Array so I can then call each result with $var[1] etc
and print output to a file. I tried doing it without Sub routines but
wasn't able to split the results. When I rapped the code into a Sub I
get these errors:

syntax error at test.pl line 7, near "@cpqlog_data"
syntax error at test.pl line 24, near "}"

How come these errors only appear when I use Sub { } ?

test.pl
use English;

It's probably a bad idea to get into the habit of using English. Almost
noone who knows Perl well uses it, so you're going to have to learn the
punctation variables anyway; and once you've learned them, it's easier
to remember one list of special cases than two.
use Warnings;

Sub LoadFile

You've been told 'sub' is case-sensitive; 'warnings' is as well. If
you're on an OS with a case-insensitive filesystem, you need to be
particularly careful about the case of module names: loading a module
with the wrong case can have rather odd effects.

You also want

use strict;

here, and you need to declare your variables with 'my'.
{
open (DAT, "<output.txt") || die("Could not open file!");

Since you're just starting to learn Perl now, you *definitely* want to
get into the habit of using lexical filehandles right away; that is,
instead of 'DAT', use a real variable. You also want to use three-arg
open, and give the reason why the open failed:

open (my $DAT, '<', 'output.txt')
|| die("Could not open 'output.txt': $!");

I would use 'or' instead of '||', and omit the parens, but that's
entirely up to you.
@cpqlog_data=<DAT>;

foreach $cpqlog (@cpqlog_data)

This is not the most straightforward way to read a file. Since you're
processing it entirely line-by-line, read it line-by-line as well.

while (my $cpqlog = said:

If you use GNUish indenting, and such a large indent, you'll quickly run
out of screen room... :)
close DAT;

One of the advantages of lexical filehandles is that they close
themselves when they go out of scope. If you're not going to check the
return value of close (not generally necessary when reading a file),
it's much more convenient to omit it.

Once you've got the syntax errors sorted out, I presume you can start
working out the logic errors on your own... :) Don't be afraid to ask
again if you get stuck.

Ben
 
T

Trev

Thanks Ben for the detailed reply, I've been able to get the output
correct, but I'm stuck on how to read $cpqlogline into an array
without writing it to a file.

my files look like this:

the perl file:

use warnings;
use strict;

my $server_file = "list.txt";
my $mactmp = "mactmp.txt";
my $maclog = "logfile.txt";

sub LoadFile
{
open(my $SRV, '<', $server_file)
or die("Could not open file: $!");

while (my $server = <$SRV>) {
chomp($server);
open (my $DAT, '<', 'output.txt')
|| die("Could not open 'output.txt': $!");

open (OTF, ">blah.txt");
print OTF $DAT;

while (my $cpqlogline = <$DAT>) {
{
chomp($cpqlogline);
if ($cpqlogline =~ /MAC/)
{
$cpqlogline =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlogline =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlogline =~ s/"\/>//i;

open (TMP, ">>$mactmp");
print TMP "$server". "," . "$cpqlogline\n";
close TMP;
}
}
}
}
close OTF;
}

sub CreateLOG
{
open (TMP, "<$mactmp");
open (LOG, ">>$maclog");
my @lines=<TMP>;
print LOG "$lines[1]";
close TMP;
close LOG;
print @lines;


unlink "blah.txt";
#unlink $mactmp;
}

LoadFile;
CreateLOG;

The list.txt file:

server1
server2
server3

The output.txt file:

<FIELD NAME="Subject" VALUE="Embedded NIC MAC Assignment"/>
<FIELD NAME="Port" VALUE="1"/>
<FIELD NAME="MAC" VALUE="00-00-00-00-00-01"/>
<FIELD NAME="Port" VALUE="2"/>
<FIELD NAME="MAC" VALUE="00-00-00-00-00-02"/>
<FIELD NAME="Port" VALUE="iLO"/>
<FIELD NAME="MAC" VALUE="00-00-00-00-00-03"/>

When the script runs it only creats the logfile with one line:
server1,00-00-00-00-00-01

When it should show:
server1,00-00-00-00-00-01
server2,00-00-00-00-00-01
server3,00-00-00-00-00-01

I see the problem is that the CreateLog only reads value[1] since the
above sub writes everything into mactmp.txt then only CreateLog is
run.

Any ideas?
 
J

Jürgen Exner

Trev said:
Thanks Ben for the detailed reply, I've been able to get the output
correct, but I'm stuck on how to read $cpqlogline into an array
without writing it to a file.

I suppose with "read into an array" you meant storing the value in an
array. Please see e.g. "perldoc -f push".

However, there is no need for that. Why not write the output file line
by line simultaneously as you process the input file line by line?

jue
 
J

Jürgen Exner

Anonymous coward said:
That makes no sense at all. Why would you want to print a filehandle

Maybe he wants to print _to_ a filehandle?
Why do you open and close a file inside a loop?

Indeed, that is an expensive operation that is better done once outside
of the loop.
Why don't you just say
print "$server,$cpglogline\n" ?

Mabye because (unless you set the default filehandle using select()) the
text will end up on the screen instead of in the file?
This program makes absolutely no sense to me. What are you trying to do
exactly? Can you give a bit more description?

It is certainly somewhat cryptic.

jue
 
T

Trev

Maybe he wants to print _to_ a filehandle?



Indeed, that is an expensive operation that is better done once outside
of the loop.


Mabye because (unless you set the default filehandle using select()) the
text will end up on the screen instead of in the file?


It is certainly somewhat cryptic.

jue

I'm connecting to many servers extracting XML data to a file, I then
only want the MAC address of Port 1 for each server. The part where I
open an existing file (output.txt) and dump content into blah.txt is
for testing, once the script it working I will remove that portion as
the output.txt file will be different for each server.

Hope that helps. I'll look into the push.

Thanks
 
B

Ben Morrow

Quoth Trev said:
Thanks Ben for the detailed reply, I've been able to get the output
correct, but I'm stuck on how to read $cpqlogline into an array
without writing it to a file.

my files look like this:

the perl file:

use warnings;
use strict;

my $server_file = "list.txt";
my $mactmp = "mactmp.txt";
my $maclog = "logfile.txt";

sub LoadFile
{
open(my $SRV, '<', $server_file)
or die("Could not open file: $!");

while (my $server = <$SRV>) {
chomp($server);
open (my $DAT, '<', 'output.txt')
|| die("Could not open 'output.txt': $!");

If you reopen output.txt every time you will start at the beginning
again.
open (OTF, ">blah.txt");
print OTF $DAT;

What are you trying to achieve here? You don't ever seem to make use of
the contents of blah.txt.
while (my $cpqlogline = <$DAT>) {
{
chomp($cpqlogline);
if ($cpqlogline =~ /MAC/)
{
$cpqlogline =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlogline =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlogline =~ s/"\/>//i;

open (TMP, ">>$mactmp");
print TMP "$server". "," . "$cpqlogline\n";
close TMP;

You don't need to keep everything in temporary files like this. Perl has
variables for keeping data in.

Here you write all the data into mactmp.txt; in fact, you write a line
for every server with every mac address. I don't think this is what you
meant.
}
}
}
}
close OTF;
}

sub CreateLOG
{
open (TMP, "<$mactmp");
open (LOG, ">>$maclog");
my @lines=<TMP>;
print LOG "$lines[1]";

Here you reopen mactmp.txt, read all the data into @lines, print the
second line (only) to maclog.txt, and throw the rest away. This is why
you only get one record in the log at the end.

I'm not quite sure how to help you at this point. You seem to be missing
several rather fundamental points about how Perl works. If I were
writing this program, I might write it something like this (completely
untested):

#!/usr/bin/perl

my $srvs = 'list.txt';
my $macs = 'output.txt';
my $log = 'logfile.txt';

# Only open each file once. We don't need any temporary files.
open my $SRVS, '<', $srvs or die "can't read '$srvs': $!";
open my $MACS, '<', $macs or die "can't read '$macs': $!";
open my $LOG, '>>', $log or die "can't append to '$log': $!";

# For each line in output.txt...
while (<$MACS>) {

# if it matches the pattern, put the VALUE field in $mac...
my ($mac) = /<FIELD NAME="MAC" VALUE="([^"]+)">/
# otherwise move on to the next line.
or next;

# Get the next server from the list.
my $srv = <$SRVS> or die "Not enough servers.\n";
chomp $srv;

# Write a line to the logfile.
print $LOG "$srv,$mac\n";
}

# Close the logfile explicitly so we can check all the data got
# written safely.
close $LOG or die "writing to '$log' failed: $!";

# Check if there are any servers left.
<$SRVS> and die "Not enough addresses.\n";

__END__

Can you understand what that's doing?

Ben
 
T

Trev

Quoth Trev <[email protected]>:


Thanks Ben for the detailed reply, I've been able to get the output
correct, but I'm stuck on how to read $cpqlogline into an array
without writing it to a file.
my files look like this:
the perl file:
use warnings;
use strict;
my $server_file = "list.txt";
my $mactmp = "mactmp.txt";
my $maclog = "logfile.txt";
sub LoadFile
{
open(my $SRV, '<', $server_file)
                   or die("Could not open file: $!");
while (my $server = <$SRV>) {
chomp($server);
open (my $DAT, '<', 'output.txt')
       || die("Could not open 'output.txt': $!");

If you reopen output.txt every time you will start at the beginning
again.
open (OTF, ">blah.txt");
print OTF $DAT;

What are you trying to achieve here? You don't ever seem to make use of
the contents of blah.txt.
while (my $cpqlogline = <$DAT>) {
           {
           chomp($cpqlogline);
           if ($cpqlogline =~ /MAC/)
           {
                   $cpqlogline =~ s/  <FIELD NAME="Subject" VALUE="//i;
                   $cpqlogline =~ s/  <FIELD NAME="MAC" VALUE="//i;
                   $cpqlogline =~ s/"\/>//i;
                   open (TMP, ">>$mactmp");
                   print TMP "$server". "," . "$cpqlogline\n";
                   close TMP;

You don't need to keep everything in temporary files like this. Perl has
variables for keeping data in.

Here you write all the data into mactmp.txt; in fact, you write a line
for every server with every mac address. I don't think this is what you
meant.
           }
   }
}
}
close OTF;
}
sub CreateLOG
{
   open (TMP, "<$mactmp");
   open (LOG, ">>$maclog");
   my @lines=<TMP>;
   print LOG "$lines[1]";

Here you reopen mactmp.txt, read all the data into @lines, print the
second line (only) to maclog.txt, and throw the rest away. This is why
you only get one record in the log at the end.

I'm not quite sure how to help you at this point. You seem to be missing
several rather fundamental points about how Perl works. If I were
writing this program, I might write it something like this (completely
untested):

    #!/usr/bin/perl

    my $srvs = 'list.txt';
    my $macs = 'output.txt';
    my $log  = 'logfile.txt';

    # Only open each file once. We don't need any temporary files.
    open my $SRVS, '<', $srvs or die "can't read '$srvs': $!";
    open my $MACS, '<', $macs or die "can't read '$macs': $!";
    open my $LOG,  '>>', $log or die "can't append to '$log': $!";

    # For each line in output.txt...
    while (<$MACS>) {

        # if it matches the pattern, put the VALUE field in $mac....
        my ($mac) = /<FIELD NAME="MAC" VALUE="([^"]+)">/
            # otherwise move on to the next line.
            or next;

        # Get the next server from the list.
        my $srv = <$SRVS> or die "Not enough servers.\n";
        chomp $srv;

        # Write a line to the logfile.
        print $LOG "$srv,$mac\n";
    }

    # Close the logfile explicitly so we can check all the data got
    # written safely.
    close $LOG or die "writing to '$log' failed: $!";

    # Check if there are any servers left.
    <$SRVS> and die "Not enough addresses.\n";

    __END__

Can you understand what that's doing?

Ben

Hi

I'm only after 1 line (MAC) per server, which is value [1] the other
MAC's I do not need. Is it possible to grep MAC from the Array (Entire
File) and pass the results into a new array which I could then
print[1] to a file. This will then extract 1 line for every server
that I have an xml file for.
 
T

Trev

I'm only after 1 line (MAC) per server, which is value [1] the other
MAC's I do not need. Is it possible to grep MAC from the Array (Entire
File) and pass the results into a new array which I could then
print[1] to a file. This will then extract 1 line for every server
that I have an xml file for.

Maybe this will help...

Read list.txt for each server listed do
read xml file into array
find results matching MAC
print the 1st MAC address only ($server,$mac[1])
go back to list.txt and do the same for next server listed in file.

That's what I'm trying to do with this perl script.
 
D

Dr.Ruud

Trev schreef:
Subject: Need help with a question.

That is not a proper Subject. Read the Posting Guidelines, as they are
posted in this group about twice a week.
 
B

Ben Morrow

Quoth Trev said:
I'm only after 1 line (MAC) per server, which is value [1] the other
MAC's I do not need. Is it possible to grep MAC from the Array (Entire
File) and pass the results into a new array which I could then
print[1] to a file. This will then extract 1 line for every server
that I have an xml file for.

Maybe this will help...

Read list.txt for each server listed do
read xml file into array
find results matching MAC
print the 1st MAC address only ($server,$mac[1])

Index [1] is the second item in an array. You need to go read a decent
beginner's Perl book: see perldoc -q book.

Ben
 
J

Jim Gibson

Erwin van Koppen said:
foreach $cpqlog (@cpqlog_data) {
{
chomp($cpqlog);

if ($cpqlog =~ /MAC/) {
$cpqlog =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlog =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlog =~ s/"\/>//i;
}
}
}

You do understand that the above code does not actually change anything in
the file, right? The substitutions in $cpqlog are just discarded at the end
of the loop.

Not quite. In Perl, the loop variable $cpqlog is an "alias" to the
array elements, so the changes to $cpqlog are applied to the members of
the array. Of course, whether or not the file is modified depends upon
what is done after the loop. (Just trying to clear up any
misunderstanding about what "discarded at the end of the loop" means.)
 
J

J. Gleixner

Trev said:
I'm only after 1 line (MAC) per server, which is value [1] the other
MAC's I do not need. Is it possible to grep MAC from the Array (Entire
File) and pass the results into a new array which I could then
print[1] to a file. This will then extract 1 line for every server
that I have an xml file for.

Maybe this will help...

Read list.txt for each server listed do
read xml file into array
find results matching MAC
print the 1st MAC address only ($server,$mac[1])
go back to list.txt and do the same for next server listed in file.

That's what I'm trying to do with this perl script.

Since you're parsing an XML file, possibly using XML::Simple
will make it much easier.

http://search.cpan.org/~grantm/XML-Simple-2.18/lib/XML/Simple.pm
 
H

Hans Mulder

Erwin said:
foreach $cpqlog (@cpqlog_data) {
{
chomp($cpqlog);

It's more efficient to chomp the whole array before the start of the loop:
chomp(@cpqlog_data);
if ($cpqlog =~ /MAC/) {
$cpqlog =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlog =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlog =~ s/"\/>//i;
}
}
}

You do understand that the above code does not actually change anything in
the file, right? The substitutions in $cpqlog are just discarded at the end
of the loop.

No they're not: this code is changing the @cpqlog_data array.

You'd still have to write that array back to the file if that's the
intent.
Oh, and you might want to get rid of the superfluous { }'s...

Or else, change the indentation to reflect the extra {}'s:

chomp(@cpqlog_data);

foreach my $cpqlog (@cpqlog_data)
{
{
if ($cpqlog =~ /MAC/)
{
$cpqlog =~ s/ <FIELD NAME="Subject" VALUE="//i;
$cpqlog =~ s/ <FIELD NAME="MAC" VALUE="//i;
$cpqlog =~ s/"\/>//i;
}
}
}

Or maybe use the magic $_ variable:

chomp(@cpqlog_data);

foreach (@cpqlog_data) {
if (/MAC/) {
s/ <FIELD NAME="Subject" VALUE="//i;
s/ <FIELD NAME="MAC" VALUE="//i;
s|"/>||i;
}
}

Hope this helps,

-- HansM
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top