Reset <> without having it fail once?

H

Hunter Johnson

I've got a filter written that needs to read into the target files
(logs) for some additional information (finding a person's numeric ID
by the name passed in) and then pull the records from the logs
matching that additional information. This can include records
earlier in the file than the record that let me match up the name to
the ID.

What I've done is close(ARGV) so that my first while(<>) fails, assign
the (saved-off) parameters back to ARGV and then start a second
while(<>) loop. If I just "last" out of the first loop and reassign
@ARGV, it finished processing the file it was in the middle of.

Is there a way to reset the <> without forcing the failure by closing
it first?

Hunter
 
J

Jeff 'japhy' Pinyan

What I've done is close(ARGV) so that my first while(<>) fails, assign
the (saved-off) parameters back to ARGV and then start a second
while(<>) loop. If I just "last" out of the first loop and reassign
@ARGV, it finished processing the file it was in the middle of.

I believe that's because you haven't changed the filehandle ARGV, which is
actually what is being read from.
Is there a way to reset the <> without forcing the failure by closing
it first?

Can you show your code so I can get a better understanding of what it is
you're doing now?

--
Jeff "japhy" Pinyan % How can we ever be the sold short or
RPI Acacia Brother #734 % the cheated, we who for every service
RPI Corporation Secretary % have long ago been overpaid?
http://japhy.perlmonk.org/ %
http://www.perlmonks.org/ % -- Meister Eckhart
 
G

Greg Bacon

: I've got a filter written that needs to read into the target files
: (logs) for some additional information (finding a person's numeric ID
: by the name passed in) and then pull the records from the logs
: matching that additional information. This can include records
: earlier in the file than the record that let me match up the name to
: the ID.

Are these large logs? Can you store the records and then search for the
interesting bits in a second pass?

: What I've done is close(ARGV) so that my first while(<>) fails, assign
: the (saved-off) parameters back to ARGV and then start a second
: while(<>) loop. If I just "last" out of the first loop and reassign
: @ARGV, it finished processing the file it was in the middle of.
:
: Is there a way to reset the <> without forcing the failure by closing
: it first?

It's a bigger-hammer approach, but here's another way:

$ cat try
#! /usr/local/bin/perl

use warnings;
use strict;

my @copy = @ARGV;
my $remember;

eval {
while (<>) {
if (/f5/) {
chomp;
$remember = $_;
close ARGV; # to get $. right
die "OKAY";
}
}
};

die "$0: $@\n" unless $@ =~ /\bOKAY\b/;

@ARGV = @copy;
while (<>) {
chomp;

print "$0: $ARGV:$.: ", ($_ eq $remember ? "eq" : "ne"), "\n";
}
continue {
close ARGV if eof;
}

$ for i in 1 2 3 4 5; do echo f$i >f$i; done

$ ./try f?
./try: f1:1: ne
./try: f2:1: ne
./try: f3:1: ne
./try: f4:1: ne
./try: f5:1: eq

Hope this helps,
Greg
 
H

Hunter Johnson

Jeff 'japhy' Pinyan said:
I believe that's because you haven't changed the filehandle ARGV, which is
actually what is being read from.


Can you show your code so I can get a better understanding of what it is
you're doing now?

Here's a test script that demonstrates the effect:

#!/usr/bin/perl

$term = shift;

@saved = @ARGV;

while (<>) {
next unless /$term;ID=(.*)/;
$ID = $1;
last;
}

@ARGV = @saved;

while (<>) {
print if /\b$ID\b/;
}

__END__

If this is the contents of files log1:
This is some test data for id 12
For id 14 too
Username JILL;ID=14
More 12 data
More 14
And just a bit more for 12

and log2:
This is some test data for id 12
For id 14 too
Username JOHN;ID=12
More 12 data
More 14
And just a bit more for 12

then the output from 'test.pl JOHN log1 log2' is:
More 12 data
And just a bit more for 12
This is some test data for id 12
More 12 data
And just a bit more for 12
This is some test data for id 12
Username JOHN;ID=12
More 12 data
And just a bit more for 12


The first two lines of the output shouldn't be there, and they do
disappear if I use 'close (ARGV)' instead of 'last'. Which is no real
hardship, but in the actual program, I end up with a test like this:

$opt{'b'} ? last : close(ARGV);

because I may not be done with the initial "recon" reading, and I was
curious if there was Another Way To Do It. It seemed like something
that could be done, but the I only found pointers to having <> fail
once in the docs.

Thanks,
Hunter
 
H

Hunter Johnson

: I've got a filter written that needs to read into the target files
: (logs) for some additional information (finding a person's numeric ID
: by the name passed in) and then pull the records from the logs
: matching that additional information. This can include records
: earlier in the file than the record that let me match up the name to
: the ID.

Are these large logs? Can you store the records and then search for the
interesting bits in a second pass?

Each log is about 20 megs, but a days' worth of logs is 320 megs, and
there's an outside chance that the script could be run against all of
the logs (currently capped at 2 gigs, but configurable). So I'd rather
not.
: What I've done is close(ARGV) so that my first while(<>) fails, assign
: the (saved-off) parameters back to ARGV and then start a second
: while(<>) loop. If I just "last" out of the first loop and reassign
: @ARGV, it finished processing the file it was in the middle of.
:
: Is there a way to reset the <> without forcing the failure by closing
: it first?

It's a bigger-hammer approach, but here's another way:

[snip code]

Yeah, that seems even less pleasing that mine. :)

Thanks,
Hunter
 
J

Jeff 'japhy' Pinyan

[posted & mailed]

#!/usr/bin/perl

$term = shift;

@saved = @ARGV;

while (<>) {
next unless /$term;ID=(.*)/;
$ID = $1;
last;
}

@ARGV = @saved;

while (<>) {
print if /\b$ID\b/;
}

I see your situation now.

my @saved = @ARGV;
while (<>) {
if ($ID) { print if /\b$ID\b/ }
elsif (/$term;ID=(.*)/) { $ID = $1; @ARGV = @saved; close ARGV; }
}

This code has not been tested, but it looks correct to me.

That uses one while loop, but does still need to close ARGV. You have to
close ARGV.

--
Jeff "japhy" Pinyan % How can we ever be the sold short or
RPI Acacia Brother #734 % the cheated, we who for every service
RPI Corporation Secretary % have long ago been overpaid?
http://japhy.perlmonk.org/ %
http://www.perlmonks.org/ % -- Meister Eckhart
 
G

Greg Bacon

: [...]
:
: The first two lines of the output shouldn't be there, and they do
: disappear if I use 'close (ARGV)' instead of 'last'. Which is no real
: hardship, but in the actual program, I end up with a test like this:
:
: $opt{'b'} ? last : close(ARGV);
:
: because I may not be done with the initial "recon" reading, and I was
: curious if there was Another Way To Do It. It seemed like something
: that could be done, but the I only found pointers to having <> fail
: once in the docs.

Use exec!

$ cat try
#! /usr/local/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 [<term> | --id=<id>] file..\n" }

sub find_id {
my $who = shift;

my @saved = @ARGV;

my $pat = qr/\Q$who\E;ID=(.*)/o;
while (<>) {
next unless /$pat/;

no warnings 'exec';
exec $0, "--id=$1", @saved;
die "$0: exec: $!";
}

die "$0: no ID found!\n";
}

sub print_matching_records {
my $id = shift;

while (<>) {
print if /\b$id\b/;
}
}

## main
die usage unless @ARGV >= 2;

my $arg = shift;

if ($arg =~ /^--id=(.+)/) {
print_matching_records $1;
}
else {
find_id $arg;
}

$ ./try JOHN log1 log2
This is some test data for id 12
More 12 data
And just a bit more for 12
This is some test data for id 12
Username JOHN;ID=12
More 12 data
And just a bit more for 12

$ ./try JILL log1 log2
For id 14 too
Username JILL;ID=14
More 14
For id 14 too
More 14

$ ./try should-not-work log1 log2
./try: no ID found!

Hope this helps,
Greg
 
H

Hunter Johnson

: I was
: curious if there was Another Way To Do It. It seemed like something
: that could be done, but the I only found pointers to having <> fail
: once in the docs.

Use exec!

[code snipped]

Say, that's slick, and no (explicit) close. Thanks!

Hunter
 
P

Peter Scott

Use exec!

Nice solution.
$ cat try
#! /usr/local/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 [<term> | --id=<id>] file..\n" }

sub find_id {
my $who = shift;

my @saved = @ARGV;

my $pat = qr/\Q$who\E;ID=(.*)/o;
while (<>) {
next unless /$pat/;

no warnings 'exec';

The line above should not be necessary; the only statement following
exec in the control flow is a die.
exec $0, "--id=$1", @saved;
die "$0: exec: $!";
}

die "$0: no ID found!\n";
}
[snip]
 
G

Greg Bacon

: In article <[email protected]>,
: (e-mail address removed) (Greg Bacon) writes:
:
: >Use exec!
:
: Nice solution.

Thanks!

: >[...]
: > while (<>) {
: > next unless /$pat/;
: >
: > no warnings 'exec';
:
: The line above should not be necessary; the only statement following
: exec in the control flow is a die.

The behavior surprised me too, but see below:

$ diff -u try.orig try.nonesuch
--- try.orig 2004-08-13 09:58:52.163202400 -0500
+++ try.nonesuch 2004-08-13 10:03:47.860649900 -0500
@@ -15,7 +15,7 @@
next unless /$pat/;

no warnings 'exec';
- exec $0, "--id=$1", @saved;
+ exec "nonesuch", "--id=$1", @saved;
die "$0: exec: $!";
}


$ ./try.nonesuch JOHN log?
../try.nonesuch: exec: No such file or directory at ./try.nonesuch
line 19, <> line 9.

$ diff -u try.orig try.nonesuch-warningson
--- try.orig 2004-08-13 09:58:52.163202400 -0500
+++ try.nonesuch-warningson 2004-08-13 10:01:37.613150700 -0500
@@ -14,8 +14,8 @@
while (<>) {
next unless /$pat/;

- no warnings 'exec';
- exec $0, "--id=$1", @saved;
+ #no warnings 'exec';
+ exec "nonesuch", "--id=$1", @saved;
die "$0: exec: $!";
}

$ ./try.nonesuch-warningson JOHN log?
Can't exec "nonesuch": No such file or directory at
../try.nonesuch-warningson line 18, <> line 9.
../try.nonesuch-warningson: exec: No such file or directory at
../try.nonesuch-warningson line 19, <> line 9.

The warnings pragma enables an autowarning on a failed exec, but I'm not
sure where this is documented.

Hope this helps,
Greg
 

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

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top