KAE
KAE

Reputation: 855

How to recursively change files in directories whose name matches a string in Perl?

I have many directories for different projects. Under some project directories, there are subdirectories named "matlab_programs". In only subdirectories named matlab_programs, I would like to replace the string 'red' with 'blue' in files ending with *.m.

The following perl code will recursively replace the strings in all *.m files, regardless of what subdirectories the files are in.

find . -name "*.m" | xargs perl -p -i -e "s/red/blue/g"

And to find the full paths of all directories called matlab_programs,

find . -type d -name "matlab_programs"

How can I combine these so I only replace strings if the files are in a subdirectory called matlab_programs?

Upvotes: 1

Views: 603

Answers (3)

reinierpost
reinierpost

Reputation: 8591

You want to find all directories named matlab_programs using

find . -type d -name "matlab_programs"

and then execute

find $f -name "*.m" | xargs perl -p -i -e "s/red/blue/g"

on all results $f. Judging by your use of xargs, there are no special characters such as spaces in your file names. so the following should work:

find `find . -type d -name "matlab_programs"` -name "*.m" |
xargs perl -p -i -e "s/red/blue/g"

or

find . -type d -name "matlab_programs" |
while read f
do
  find $f -name "*.m" | xargs perl -p -i -e "s/red/blue/g"
done |
xargs perl -p -i -e "s/red/blue/g"

Incidentally, I'd use single quotes here; I always use them whenever the quoted string is to be taken literally.

Upvotes: 1

Sobrique
Sobrique

Reputation: 53478

Perl has the excellent File::Find module, that lets you specify a callback to be called on each file.

So you can specified a complex compound criteria, like this:

#!/usr/bin/env perl

use strict;
use warnings;

use File::Find;

sub find_files {
    next unless m/\.m\z/;    # skip any files that don't end in .m
    if ( $File::Find::dir =~ m/matlab_programs$/ ) {
        print $File::Find::name, " found\n";
    }
}

find( \&find_files, "." );

And then you can do whatever you wish with the files you find - like opening/text replacing and closing.

Upvotes: 3

mob
mob

Reputation: 118595

Do you have bash? The $(...) syntax works like backticks (the way both the shell and Perl use them) but they can be nested.

perl -pi -e s/red/blue/g $(find $(find . -type d -name matlab_programs) -type f -name \*.m)

Many flavors of find also support a -path pattern test, so you can just combine your filename conditions into that argument

perl -pi -e s/red/blue/g $(find . -type f -path \*/matlab_programs/\*.m)

Upvotes: 1

Related Questions