Как мне выполнить несколько замен содержимого каждого файла в нескольких каталогах?

#perl #file-io #substitution

#perl #file-io #замена

Вопрос:

Мне нужно выполнить несколько замен содержимого файлов в разных каталогах (не рекурсивно).

Например, для всех файлов в каталоге /foo/bar:

 s/blue/red/;
s/green/yellow/;
  

И для всех файлов в каталоге / foobar / bar:

 s/orange/black/;
  

Каков наиболее эффективный способ написания этого скрипта?

На данный момент все мои сценарии подстановки являются однострочными, и поэтому я в значительной степени в неведении о том, как полностью интегрировать их в полноценный скрипт.


Я включил фрагмент из моего нефункционального тестового скрипта ниже, если он может быть полезен.

 opendir(DIR, $dir) or die "Cannot open directory: $!n";
my @files = readdir(DIR);
closedir(DIR);

foreach(@files) {
    my $filename = $_;   
    open(FILE, $filename);

    while (my $data = <FILE>) {
        # Substitution lines
        }
    close(FILE);
    }
  

Ответ №1:

Вы могли бы попытаться решить проблему с помощью однострочного perl:

 perl -i".bak" -n -e 's/blue/glue/g; print;' *
  

Вы могли бы использовать many -e или, альтернативно, использовать many s/// . Это также создает резервные копии текущих файлов.

Надеюсь, это поможет.

Комментарии:

1. @fallenland Спасибо за ваш ответ, но у меня уже есть замены в нескольких однострочниках. Я пытаюсь объединить их в один скрипт, который будет chdir и выполнять их в нескольких каталогах. Прошу прощения, если мой вопрос был неясен.

Ответ №2:

Обход каталога, вероятно, является большим узким местом, поэтому вы должны попытаться сделать это только один раз.

 find . -type f -exec perl -i".bak" -pe '
  if($ARGV =~ m%^./foo/bar/%){s/blue/red/g;s/green/yellow/g;}
  s/orange/black/ if($ARGV =~ m%^./foobar/bar/%' {} ;
  

Ответ №3:

Я обновил код, чтобы он соответствовал тому, что вы хотите, вам придется использовать модуль, который анализирует аргументы командной строки, такие как Getopt::Long; Я не уверен в синтаксисе, но вам понадобится что-то, что позволяет создавать массивы строк на обоих концах; я уверен, что естьэто лучший способ обработки аргументов, позволяющий считывать их как ключ, сопоставление значений.

 #!/usr/bin/perl
# invoke with:
# script.pl <name part to match> <name part to replace> <directory to examine> <directory to examine...>
use Getopt::Long;
my %opts = ();

... read in match keys / values ...

my $dir = shift;
while(-d $dir){
    @files_in_dir = <$dir/*>;
    foreach my $file (@files_in_dir){
        my @lines = ();
        open(my $IN, '<', $dir/$file);
        while(<$IN>){
            foreach $i (1..#@$opt{match}){
                if(/@{$opt{match}}[$i]/){
                    s/@{$opt{match}}[$i]/@{$opt{replace}}[$i]/;
                }  
            }
            push @lines, $_;
        }
        close $IN;
        open(my $OUT, '>', $dir/$file);
        print $OUT "$_" foreach(@lines);
        close $OUT;
    }
    $dir = shift;
}