动态Perl使用内部反引号中的grep查找和替换

时间:2012-01-24 01:56:52

标签: perl bash command-line replace backticks

我正在尝试进行动态搜索,并在命令行中使用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的目标文件应该是*。

我根据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. 将上述更改写入输入文件,而不修改输入文件中的任何其他内容。
  4. 运行我的脚本后,输入文件中的HTML代码完全乱码,它会切断文件中每行的前几个字符。这很奇怪,因为我确定$ URL_Last只在每个文件中出现一次,因此它应该只匹配一次并替换一次。这是否是由于滥用搜索功能造成的?

3 个答案:

答案 0 :(得分: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命令不会被执行,因为它只是一个字符串,反引号不是元字符。替换中的文本将充当双引号字符串中的内容。您需要/e标志来执行shell命令:

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" *`

这可能会也可能不会达到预期效果,具体取决于您是否希望在字符串中添加换行符。我不确定参数列表是否会被视为列表或字符串。

答案 1 :(得分:2)

看起来你要做的就是......

  1. 在树中查找包含给定字符串的文件。
  2. 使用该文件构建网址。
  3. 使用该网址替换字符串中的内容。
  4. 你有三个部分,你可以将它们组合成一个正则表达式,但通过三个步骤完成它会容易得多。当你需要添加它时,你不会在一周内讨厌自己。

    第一步是获取文件名。

    # 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 :(得分: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;
            }
    }