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

I have this script in which I am trying to clean up the tmp file output and use only arrays and variables. It does work, I created it a while back.
open my $bpl_fh, '<', '/tmp/bpl.txt' or die $!; chomp(my @words = <$bpl_fh>); close $bplist_fh; my $pattern = join '|', @words; my @lines; open my $fh, '<', '/tmp/lst.txt' or die "unable to open file '$file' f +or reading : $!"; open my $fh2, '>', '/tmp/lst2a.txt' or die "unable to open file '$file +' for reading : $!"; while (<$fh>) { push @lines, $_ if /$pattern/; } print $fh2 "$_\n" foreach @lines; close $fh2;
So I have made bpl and bpout into an arrays but I am unsure of how to write the while statement. I want it to perform the same task as the above script but without using the /tmp/files to read from and output
chomp(my @words = @bpl); my $pattern = join '|', @words; #my @lines; my $item; while (@bpout) { my $item = (@bpout); push $item, $_ if /$pattern/; } print "$_\n" foreach $item;
Does anyone have suggestions?

Replies are listed 'Best First'.
Re: removing the need for tmp files from script
by hippo (Archbishop) on Jul 22, 2016 at 16:29 UTC
    push $item, $_ if /$pattern/;

    Pushing to a scalar (which isn't an array ref) is an error. Just use an array instead. Same goes for your foreach postfix. Don't declare $item inside and outside the loop unless you intend them to be different variables. And maybe think about an alternative style of indenting? Good luck.

Re: removing the need for tmp files from script
by Laurent_R (Canon) on Jul 22, 2016 at 17:12 UTC
    Assuming I understand what you're trying to do, it seems to me this would be easier:
    for (@bpout) { print "$_\n" if /$pattern/; }
Re: removing the need for tmp files from script
by BillKSmith (Monsignor) on Jul 23, 2016 at 22:51 UTC
    Another option is to change your open statement so the filehandle of your temp file refers to an "in-memory" file. No other changes are required. Temporary data would go to a scalar instead of a disk. Refer to 'open' in perlfunc for more on in-memory files.
    Bill
      Fantastic information. I was wondering though, you don't seem to be able to use the "filehandle" variable like a real file in a script. For example: I have this beginnerish script
      open my $fh2, '<', '/tmp/mlist.txt' or die "unable to open file '$file +' for reading : $!"; open my $fh9, '>', '/tmp/listk.txt' or die "unable to open file '$file +' for reading : $!"; while (<$fh2>) { my @fields = split(',', $_); local $" = ','; if ($fields[2] eq '1' || $fields[2] eq '0') { print $fh9 "@fields[4]\n" if /MONTHLY/ && !/,-,/; } } close $fh2; close $fh9;
      This is clumsy but it works. However, if I try to use the variable assigned to a filehandle in the same way:
      open my $bpfh1, '<', \$forbpjoutput or die $!; while (<$bpfh1>) { my @fields = split(',', $_); my $fields; local $" = ','; if ($fields[2] eq '1' ||$fields[2] eq '0') { print "THESE ARE THE>>> $fields[4]\n" if /MONTHLY/ && !/, +-,/; close $bpfh1; } }
      it does work but what happens is that it never stops printing the "file" or the fields4 It continues until out of memory. Is there a way to do this differently? This is probably a very basic question, but I don't have much experience with filehandles which are variables.
        Hi novice2015

        The link to open that BillKSmith mentioned in his post has the following text.

        # in-memory files 21. open(my $memory, ">", \$var) 22. or die "Can't open memory file: $!"; 23. print $memory "foo!\n"; # output will appear in $var
        This shows how to write to an in-memory file.

        To read from an in memory file, you would do this:

        #!/usr/bin/perl use strict; use warnings; my $file = 'here are some words I want '; open my $fh, '<', \$file; chomp(my @words = <$fh>); my $pattern = join "|", @words; print $pattern;
        That would be a way to not need the tmp. file.
        I cannot duplicate your problem. When I run your code on my system (perl v5.20.2 on Windows 10), it terminates with no error messages and no output. It I use warnings, I get several copies of a warning about uninitialized value in $bpfh1. If I declare $forbpjoutput as a lexical variable and define it as a null string, I get no messages and no output even using strict and warnings. (This is exactly what I expect).
        use strict; use warnings; my $forbpjoutput = q(); open my $bpfh1, '<', \$forbpjoutput or die $!; ... # Exactly as in your post.

        As Cristoforo pointed out, you probably did not intend to input from a null file. I cannot guess what you really did intend to do.

        Bill
Re: removing the need for tmp files from script
by haukex (Archbishop) on Jul 30, 2016 at 14:15 UTC

    Hi novice2015,

    I don't think in-memory files like you are trying to use here are necessary, they overcomplicate the solution. Just use a normal Perl array to hold the data in between loops.

    What is unclear to me is this: If you don't want to use any of the files in /tmp, where will the data that you are currently reading from /tmp/bpl.txt and /tmp/lst.txt come from? In order to answer your original question, I could assume the data you are reading from those two files is available in @words and @data, respectively. Then I can answer the original question and rewrite your original script like so:

    # get @words and @data from somewhere my $pattern = join '|', @words; my @lines; for (@data) { push @lines, $_ if /$pattern/; } # do something with @lines

    By the way, are you using warnings and strict? In your original code you wrote open my $bpl_fh, ... but then close $bplist_fh;. Also, in your later posts it looks like you're closing $bpfh1; too soon, inside the loop, which should give you a warning. So always Use strict and warnings!

    Now, if I understand your later posts, you then want to take the lines that you've previously matched with /$pattern/ and stored in @lines and operate on those. If I take your code from here and rewrite it, I get:

    for (@lines) { my @fields = split /,/; if ( $fields[2] eq '1' || $fields[2] eq '0' ) { print "THESE ARE THE>>$fields[4]\n" if /MONTHLY/ && !/,-,/; } }

    But then my question is, do you need @lines at all? You could skip the creation of that array and do:

    my $pattern = join '|', @words; for (@data) { next unless /$pattern/; my @fields = split /,/; if ( $fields[2] eq '1' || $fields[2] eq '0' ) { print "THESE ARE THE>>$fields[4]\n" if /MONTHLY/ && !/,-,/; } }

    If you have further questions, I strongly recommend you read Short, Self Contained, Correct Example and How do I post a question effectively? - without input data, runnable code, expected output or exact error messages, it's a guessing game.

    Hope this helps,
    -- Hauke D