В приведенном ниже коде используется Perl редактирование на месте , позволяющее изменять только те файлы, которые необходимо изменить, и оставляет резервные копии оригиналов. Например, если он обновляет foo.php
, оригинал будет в foo.php.bak
.
Начнем с типичного фронта. Мы будем использовать модуль File :: Find для поиска файлов HTML и PHP на любой глубине, начиная по умолчанию в текущем каталоге или во всех каталогах, указанных в командной строке.
#! /usr/bin/perl
use warnings;
use strict;
use File::Find;
sub usage { "Usage: $0 [ directory .. ]\n" }
В read_filelist
мы получаем список преобразований, использующих их для генерации двух битов кода:
- скомпилированное регулярное выражение, соответствующее любой строке, содержащей хотя бы одно старое имя файла, и
- скомпилированная подпрограмма, которая заменяет старые имена новыми.
Обратите внимание на использование \b
, которое соответствует только на границах слов, и quotemeta
, которое экранирует любые метасимволы регулярных выражений. Они ограничивают замены только буквальными вхождениями старых имен файлов.
Это немного длинно, так что простите полосу прокрутки.
sub read_filelist {
my $path = "filelist.txt";
open my $fh, "<", $path
or die "$0: open $path: $!";
my $code = q[
sub {
while (<>) {
];
my @old;
my $errors;
while (<$fh>) {
next if $. == 1 && /OldName.*NewName/;
my($old,$new) = split;
unless (defined($old) && defined($new)) {
warn "$0: $path:$.: missing filename\n";
++$errors;
}
my($qmold,$qmnew) = map quotemeta($_) => $old, $new;
$code .= " s/\\b$qmold\\b/$qmnew/gi;\n";
push @old, $old;
}
die "$0: will not continue\n" if $errors;
$code .= q[ print;
}
}];
local $@;
print $code, "\n";
my $sub = eval $code;
die "$0: should not happen: $@" if $@;
my $pattern = '\b(' .
join("|", map quotemeta, @old) .
')\b';
#print $pattern, "\n";
my $regex = eval { qr/$pattern/oi };
die "$0: should not happen: $@" if $@;
($regex, $sub);
}
Здесь мы запоминаем корневые каталоги для начала поиска и читаем filelist.txt
. Редактирование на месте в Perl требует, чтобы обновляемые файлы находились в специальном массиве @ARGV
.
my @dirs = @ARGV ? @ARGV : ".";
my($has_old,$replace) = read_filelist;
Подпункт needs_update
- это рабочий, который File::Find::find
использует для проверки необходимости изменения данного файла. Мы можем поместить все файлы PHP и HTML в @ARGV
, но в случаях, когда код ничего не меняет, вы все равно получите файлы резервных копий для всего.
sub needs_update {
return unless /\.(?:php|html)$/i;
open my $fh, "<", $_
or warn("$0: open $_: $!"), return;
while (<$fh>) {
if (/$has_old/) {
push @ARGV, $File::Find::name;
return;
}
}
},
Для основного события мы очищаем @ARGV
, используем needs_update
, чтобы добавить соответствующие файлы PHP и HTML, и раскрыть скомпилированную подпрограмму на них.
# set up in-place editing
@ARGV = ();
find \&needs_update, @dirs;
die "$0: nothing to do!\n" unless @ARGV;
$^I = ".bak";
$replace->();
__END__
Пара заметок:
- Приведенный выше код пытается выполнить все замены в порядке, указанном в
filelist.txt
.
- Он не пытается ограничить область видимости элементами
<link>
и <script>
. Чтобы сделать это правильно, требуется анализатор HTML. Если имена источников Javascript в filelist.txt
иногда не следует заменять, я могу обновить этот ответ дополнительным механизмом, но выполнение добавит еще больше кода.
В Windows вставьте код (без моего текущего комментария) в файл с расширением .pl
, а затем запустите его как
C:\> fixjs.pl
для исправления файлов HTML и PHP под текущим каталогом. Чтобы обработать один или несколько каталогов в другом месте, запустите
C:\> fixjs.pl D:\some\dir E:\another