Search code examples
linuxbashshellperl

Perl script to search a word inside the directory


I'am looking for a perl script to grep for a string in all files inside a directory .

bash command . Code: grep -r 'word' /path/to/dir


Solution

  • This is a fairly canonical task while I couldn't find straight answers with a possibly easiest and simples tool for the job, the handy Path::Tiny

    use warnings;
    use strict;
    use feature 'say';
    
    use Data::Dump;  # dd
    use Path::Tiny;  # path 
    
    my $dir = shift // '.';
    
    my $pattern = qr/word/;
    
    my $ret = path($dir)->visit( 
        sub { 
            my ($entry, $state) = @_; 
            return if not -f; 
         
            for ($entry->lines) {
                if (/$pattern/) {
                    print "$entry: $_";
                    push @{$state->{$entry}}, $_;  
                }   
            }   
        },  
        { recurse => 1 } 
    ); 
    
    dd $ret;  # print the returned complex data structure
    

    The way a file is read here, using lines, is just one way to do that. It may not be suitable for extremely large files as it reads all lines at once, where one better read line by line.

    The visit method is based on iterator, which accomplishes this task cleanly as well

    my $iter = path($dir)->iterator({ recurse => 1 });
    
    my $info;
    
    while (my $e = $iter->()) { 
        next if not -f $e; 
        
        # process the file $e as needed
        #/$pattern/ and push @{$info->{$e}}, $_ and print "$e: $_" 
        #    for $e->lines 
    } 
    

    Here we have to provide a data structure to accumulate information but we get more flexibility.

    The -f filetest used above, of a "plain" file, is still somewhat permissive; it allows for swap files, for example, which some editors keep during a session (vim for instance). Those will result in all kinds of matches. To stay with purely ASCII or UTF-8 files use -T test.


    Otherwise, there are libraries for recursive traversal and searching, for example File::Find (or File::Find::Rule) or Path::Iterator::Rule.

    For completeness, here is a take with the core File::Find

    use warnings;
    use strict;
    use feature 'say';    
    use File::Find;
    
    my @dirs = @ARGV ? @ARGV : '.';
    
    my $pattern = qr/word/;
    
    my %res;
    find( sub {
            return if not -T;  # ASCII or UTF-8 only
    
            open my $fh, '<', $_ or do {
                warn "Error opening $File::Find::name: $!";
                return;
            };
    
            while (<$fh>) { 
                if (/$pattern/) { 
                    chomp;
                    push @{$res{$File::Find::name}}, $_
                }
            }
        }, @dirs
    );
    
    for my $k (keys %res) { 
        say "In file $k:";
        say "\t$_" for @{$res{$k}};
    }