Динамический Perl находит и заменяет, используя grep внутри backticks - PullRequest
1 голос
/ 24 января 2012

Я пытаюсь выполнить динамический поиск и заменить на Perl в командной строке, причем часть текста замены является выводом команды grep в обратных галочках.Это можно сделать в командной строке, или мне нужно написать сценарий для этого?

Вот команда, которая, как я думал, сработает.Я думал, что Perl будет рассматривать обратные пометки как подстановку команд, но вместо этого он просто обрабатывает обратные помехи и содержимое внутри них как строку:

perl -p -i -e 's/example.xml/http:\/\/exampleURL.net\/`grep -ril "example_needle" *`\/example\/path/g' `grep -ril "example_needle" *`

ОБНОВЛЕНИЕ:

Спасибо за полезнуюответы.Да, в моей оригинальной однострочной опечатке была опечатка: предполагается, что целевой файл grep *.

Я написал небольшой скрипт на основе примера Шеврна, но у меня непонятные результаты.Вот сценарий, который я написал:

 #!/usr/bin/env perl -p -i

my $URL_First = "http://examplesite.net/some/path/";
my $URL_Last = "/example/example.xml";

my @files = `grep -ril $URL_Last .`;
chomp @files;

foreach my $val (@files) {
        @dir_names = split('/',$val);

        if(@dir_names[1] ne $0) {

            my $url = $URL_First .  @dir_names[1] . $URL_Last;

            open INPUT, "+<$val" or die $!;

            seek INPUT,0,0;

            while(<INPUT>) {
                    $_ =~ s{\Q$URL_Last}{$url}g;
                    print INPUT $_;
                    }
            close INPUT;
            }
    }

В основном я пытаюсь сделать следующее:

  1. Найти файлы, содержащие $ URL_Last.
  2. Заменить $ URL_Lastс $ URL_First плюс имя каталога, в котором находится соответствующий файл, плюс $ URL_Last.
  3. Запишите вышеуказанное изменение во входной файл, не изменяя ничего во входном файле.

После запуска моего скрипта он полностью исказил код HTML во входном файле и обрезал первые несколько символов каждой строки в файле.Это странно, потому что я точно знаю, что $ URL_Last встречается в каждом файле только один раз, поэтому его следует сопоставлять только один раз и заменять один раз.Это вызвано неправильным использованием функции поиска?

Ответы [ 3 ]

2 голосов
/ 24 января 2012

Кажется, что ты пытаешься сделать это ...

  1. Найти в дереве файл, содержащий заданную строку.
  2. Используйте этот файл для создания URL.
  3. Заменить что-то в строке этим URL.

У вас есть три части, и вы можете объединить их в одно регулярное выражение, но гораздо проще сделать это за три шага. Вы не будете ненавидеть себя через неделю, когда вам нужно добавить к этому.

Первый шаг - получить имена файлов.

# grep -r needs a directory to search, even if it's just the current one
my @files = `grep -ril $search .`;

# strip the newlines off the filenames
chomp @files;

Тогда вам нужно решить, что делать, если вы получите более одного файла из grep. Я оставлю этот выбор на ваше усмотрение, я просто возьму первый.

my $file = $files[0];

Затем создайте URL. Достаточно просто ...

# Put it in a variable so it can be configured
my $Site_URL = "http://www.example.com/";

my $url = $Site_URL . $file;

Чтобы сделать что-нибудь более сложное, вы должны использовать URI .

Теперь поиск и замена тривиальны.

# The \Q means meta-characters like . are ignored.  Better than
# remembering to escape them all.
$whatever =~ s{\Qexample.xml}{$url}g;

Вы хотите редактировать файлы, используя -p и -i. К счастью, мы можем эмулировать эту функциональность.

#!/usr/bin/env perl
use strict;
use warnings; # never do without these

my $Site_URL   = "http://www.example.com/";
my $Search     = "example-search";
my $To_Replace = "example.xml";

# Set $^I to edit files. With no argument, just show the output
# script.pl .bak  # saves backup with ".bak" extension
$^I = shift;

my @files = `grep -ril $Search .`;
chomp @files;
my $file = $files[0];

my $url = $Site_URL . $file;

@ARGV = ($files[0]);  # set the file up for editing
while (<>) {
    s{\Q$To_Replace}{$url}g;
}
2 голосов
/ 24 января 2012

Вы должны использовать другой разделитель для s///, чтобы вам не нужно было экранировать косую черту в URL:

perl -p -i -e '
s#example.xml#http://exampleURL.net/`grep -ril "example_needle"`/example/path#g'
    `grep -ril "example_needle" *`

Ваша команда grep внутри регулярного выражения не будет выполнена, так какэто просто строка, и обратные галочки не являются метасимволами.Текст внутри замещения будет действовать так, как если бы он был внутри строки в двойных кавычках.Вам понадобится флаг /e для выполнения команды оболочки:

perl -p -i -e '
s#example.xml#
    qq(http://exampleURL.net/) . `grep -ril "example_needle"` . qq(/example/path)
    #ge'
    `grep -ril "example_needle" *`

Однако что именно вы ожидаете от этой команды grep?В нем отсутствует целевой файл.-l будет печатать имена файлов для соответствующих файлов, а grep без целевого файла будет использовать стандартный ввод, который, я подозреваю, не будет работать.

Если это опечатка, и вы намеревались использовать тот же grepЧто касается вашего списка аргументов, почему бы не использовать @ARGV?

perl -p -i -e '
s#example.xml#http://exampleURL.net/@ARGV/example/path#g'
    `grep -ril "example_needle" *`

Это может или не может делать то, что вы ожидаете, в зависимости от того, ожидаете ли вы, что в строке будут символы новой строки.Я не уверен, что список аргументов будет считаться списком или строкой.

0 голосов
/ 26 января 2012

Все ответы очень помогли мне написать сценарий, который помог мне.Вчера я действительно нашел решение для bash-скрипта, но хотел опубликовать Perl-ответ, если кто-нибудь еще найдет этот вопрос через Google.

Сценарий, который @TLP разместил на http://codepad.org/BFpIwVtz, является альтернативным способомделая это.

Вот что я закончил писать:

#!/usr/bin/perl

use Tie::File;

my $URL_First = 'http://example.com/foo/bar/';
my $Search = 'path/example.xml';
my $URL_Last = '/path/example.xml';

# This grep returns a list of files containing "path/example.xml"
my @files = `grep -ril $Search .`;
chomp @files;

foreach my $File_To_Edit (@files) {

# The output of $File_To_Edit looks like this: "./some_path/index.html"
# I only need the "some_path" part, so I'm going to split up the output and only use @output[1] ("some_path")
    @output = split('/',$File_To_Edit);

# "some_path" is the parent directory of "index.html", so I'll call this "$Parent_Dir"
    my $Parent_Dir = @output[1];

# Make sure that we don't edit the contents of this script by checking that $Parent_Dir doesn't equal our script's file name.
    if($Parent_Dir ne $0) {

            # The $File_To_Edit is "./some_path/index.html"
            tie @lines, 'Tie::File', $File_To_Edit or die "Can't read file: $!\n";
            foreach(@lines) {
                    # Finally replace "path/example.xml" with "http://example.com/foo/bar/some_path/path/example.xml" in the $File_To_Edit
                    s{$Search}{$URL_First$Parent_Dir$URL_Last}g;
                    }
            untie @lines;
            }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...