Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Need help understanding code snippet with "grep -f"

by fifaltra (Acolyte)
on Dec 23, 2015 at 02:42 UTC ( #1150988=perlquestion: print w/replies, xml ) Need Help??

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

Dear Perl Monks,

when I first started using perl, I had a friend kickstart me with some scripts that I needed at the time. I then went and kept modifying and rearranging those scripts to fit new problems I wanted to solve with perl. Due to this, I still often have snippets in my code that I just use as a black box, which is fine, until I want the behaviour to change slightly.

So now I have this snippet here:

my @files = grep -f, map "$_/vp.o$id", qw/archi1 archiv/;

My guess what this does is: The qw makes an array, which map then takes to create another array that contains the string with $_ replaced by the entries of the first array. And then grep takes this and magic happens, and I get an array of files, if there are files matching this pattern.

So, first question: Am I on the right path here?

Now, what I want to do is basically this:

my @files = grep -f, map "$_/*vp.o$id", qw/ archiv/;

But perl does not understand me, which I also did not really expect in this case. (I hope you do, but just in case, I want all files that match this pattern, just as the shell would work. I realise just now that of course grep in the shell would not work like this, so in the shell I would just grep the first and the second part separately...)

So my second and third questions are: What exactly does this grep -f do? I couldn't find any documentation on this. And how do I have to modify the line so that it finds all files in the directory archiv that match the pattern ".e$id"?

Here's more of my code for context:

#!/usr/bin/perl use strict; use warnings; my ($id) = @ARGV; my $fn2 = do { my @files = grep -f, map "$_/*vp.e$id", qw/ archiv/; print @files . " error files found\nless $files[0]\n" unless @files +== 0; $files[0]; }; my $fn = do { my @files = grep -f, map "$_/*vp.o$id", qw/ archiv/; die @files . " matching files found" unless @files == 1; $files[0]; }; open my $fh, '<', $fn or die qq{Couldn't open "$fn" for reading: $!};

Replies are listed 'Best First'.
Re:Need help understanding code snippet with "grep -f"
by GrandFather (Saint) on Dec 23, 2015 at 03:01 UTC

    The -f is a file operator that returns true if the file (by default given in $_) exists so the grep is testing to see if named files exist and dropping any that don't exist out of the list.

    grep can use either grep {...} @list or grep ..., @list syntax. You are using the second variant.

    grep {-f} map {...} @list;

    might be clearer.

    Update: Oh, and to "find all the files" you need something like glob:

    my @files = grep {-f} map {glob "$_/*vp.o$id"} qw/ archiv/;

    Although if you are using glob the grep is probably redundant.

    Premature optimization is the root of all job security
      my @files = grep {-f} map {glob "$_/*vp.o$id"} qw/ archiv/;
      I figure the grep -f and the map are both not necessary.
      Shouldn't this work also?
      my @files = glob {"archiv/*vp.o$id"};
        I figure the grep -f and the map are both not necessary. Shouldn't this work also?
        my @files = glob {"archiv/*vp.o$id"};

        No. glob does not check the type of the file. -f does:

        /tmp>touch foo.txt /tmp>mkdir bar.txt /tmp>ls -lF [...] drwxr-xr-x 2 alex users 40 Dec 23 10:16 bar.txt/ -rw-r--r-- 1 alex users 0 Dec 23 10:16 foo.txt [...] /tmp>perl -E 'say for glob "*.txt"' bar.txt foo.txt /tmp>perl -E 'say for grep -f,glob "*.txt"' foo.txt /tmp>


        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        The map is useful if you have a list containing more than one item. As I said in my OP the grep is redundant if you are using glob.

        Premature optimization is the root of all job security
      Thanks a lot for the explanation. I changed it to just glob now. Works perfectly and I learned something new today! Yay!
Re: Need help understanding code snippet with "grep -f"
by Anonymous Monk on Dec 23, 2015 at 02:59 UTC

    My guess what this does is:

    Don't guess, read the docs, grep is not like grep, grep is like foreach that filters out wanted list items

      Well, I did read it, but I couldn't figure out what the -f does there, and how the behaviour of grep described in the docs would give me a list of existing files. But that's probably cleared up now. Is there also documentation on this "-f" thing? Because it's really hard to google for something as short as "-f"...

        Reading this reminded me .. I had the exact same thing trip me up. -f is not a switch to the grep function but rather an independently defined function that just happens to be used as an argument here. But the unix-familiar mind sees that and thinks "grep -f" is a special form of "grep" and seeks the answer in the grep documentation. And doesn't find it.

        Using the block form of grep might have made this clearer.

        @files = grep { -f } glob("~/data/*");
        But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1150988]
Approved by GrandFather
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2021-04-13 14:31 GMT
Find Nodes?
    Voting Booth?

    No recent polls found