#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;
}