2012-01-24 2 views
1

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

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

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

UPDATE:

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

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

#!/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 встречается только один раз в каждом файле, поэтому его следует сопоставлять только один раз и заменять один раз. Это связано с неправильным использованием функции поиска?

+1

Если я не ошибаюсь, 'Grep -ril«example_needle»' не хватает целевого файла, так как вы не можете напечатать имя файла на стандартный ввод. Возможно, вам стоит просто попытаться объяснить, что вы пытаетесь сделать. – TLP

+0

найдите время, чтобы создать небольшой файл образца, который вы можете редактировать в своем вопросе, и показать, что вам нужно в качестве вывода. В противном случае мы просто догадываемся. Удачи. – shellter

+0

Иск определенно не нужен, поскольку позиция дескриптора файла уже будет в начале при открытии файла. Что касается вашего описания проблемы, вы обнаружите, что 1 пример стоит более 10 эссе. Я предполагаю, что вы хотите, например, открыть 'foo/bar.html', заменить'/example/example.xml' на 'http: // examplesite.net/some/path/foo/example/example.xml'? – TLP

ответ

0

Ответы каждого были очень полезны для написания сценария, который заработал для меня. Я на самом деле нашел решение для сценария 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; 
      } 
    } 
2

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

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

Ваш grep команда в регулярном выражении не будет выполнено, так как это просто строка, и backticks не являются метасимволами. Текст внутри подстановки будет действовать так, как если бы он был внутри строки с двойными кавычками. Вы должны были бы в /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 без целевого файла будет использовать stdin, который, как я подозреваю, не будет работать.

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

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

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

2

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

  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; 
} 
+0

Использование '-pi' в shebang приятно, но в этом случае я бы не пошел так. Если вы собираетесь использовать длинную версию, сохранение некоторых сообщений будет спорным. Вы уменьшаете производительность, выполняя 'grep' для каждой строки в файле.В этом случае я просто добавлял 'while (<>)' и 'print' вокруг подстановки, а затем устанавливал' $^I' для редактирования на месте. – TLP

+0

О, и, возможно, '$^I = shift; push @ARGV, $ files [0]; ' – TLP

+0

@TLP О, хорошая точка о grep! Я собираюсь в wiki сообщества, чтобы вы могли исправить это, если хотите. – Schwern