动态 Perl 在反引号内使用 grep 查找和替换

发布于 2024-12-28 12:02:34 字数 1343 浏览 1 评论 0原文

我正在尝试进行动态搜索并在命令行上用 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. 将上述更改写入输入文件,而不修改输入文件中的其他任何内容。

运行我的脚本后,它完全混淆了输入文件中的 HTML 代码,并截断了文件中每行的前几个字符。这很奇怪,因为我确信 $URL_Last 在每个文件中只出现一次,所以它应该只匹配一次并替换一次。这是由于滥用seek函数造成的吗?

I am trying to do a dynamic search and replace with Perl on the command line with part of the replacement text being the output of a grep command within backticks. Is this possible to do on the command line, or will I need to write a script to do this?

Here is the command that I thought would do the trick. I thought that Perl would treat the backticks as a command substitution, but instead it just treats the backticks and the content within them as a string:

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

UPDATE:

Thanks for the helpful answers. Yes, there was a typo in my original one-liner: the target file of grep is supposed to be *.

I wrote a small script based on Schewrn's example, but am having confusing results. Here is the script I wrote:

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

Basically what I am trying to do is:

  1. Find files that contain $URL_Last.
  2. Replace $URL_Last with $URL_First plus the name of the directory that the matched file is in, plus $URL_Last.
  3. Write the above change to the input file without modifying anything else in the input file.

After running my script, it completely garbled the HTML code in the input file and it cut off the first few characters of each line in the file. This is strange, because I know for sure that $URL_Last only occurs once in each file, so it should only be matched once and replaced once. Is this being caused by a misuse of the seek function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

三生一梦 2025-01-04 12:02:34

您应该为 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" *`

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

You should use another delimiter for s/// so that you don't need to escape slashes in the URL:

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

Your grep command inside the regex will not be executed, as it is just a string, and backticks are not meta characters. Text inside a substitution will act as though it was inside a double quoted string. You'd need the /e flag to execute the shell command:

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

However, what exactly are you expecting that grep command to do? It lacks a target file. -l will print file names for matching files, and grep without a target file will use stdin, which I suspect will not work.

If it is a typo, and you meant to use the same grep as for your argument list, why not use @ARGV?

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

This may or may not do what you expect, depending on whether you expect to have newlines in the string. I am not sure that argument list will be considered a list or a string.

不一样的天空 2025-01-04 12:02:34

看起来您想要做的是...

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

It seems like what you're trying to do is...

  1. Find a file in a tree which contains a given string.
  2. Use that file to build a URL.
  3. Replace something in a string with that URL.

You have three parts, and you could jam them together into one regex, but it's much easier to do it in three steps. You won't hate yourself in a week when you need to add to it.

The first step is to get the filenames.

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

Then you need to decide what to do if you get more than one file from grep. I'll leave that choice up to you, I'm just going to take the first one.

my $file = $files[0];

Then build the URL. Easy enough...

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

my $url = $Site_URL . $file;

To do anything more complicated, you'd use URI.

Now the search and replace is trivial.

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

You want to edit files using -p and -i. Fortunately we can emulate that functionality.

#!/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;
}
面如桃花 2025-01-04 12:02:34

每个人的回答都对我写出适合我的剧本非常有帮助。我昨天实际上找到了一个 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;
            }
    }

Everyone's answers were very helpful to my writing a script that wound up working for me. I actually found a bash script solution yesterday, but wanted to post a Perl answer in case anyone else finds this question through Google.

The script that @TLP posted at http://codepad.org/BFpIwVtz is an alternative way of doing this.

Here is what I ended up writing:

#!/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;
            }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文