【问题标题】:Multiline search replace with Perl多行搜索替换为 Perl
【发布时间】:2010-11-05 01:43:42
【问题描述】:

我知道这种问题以前已经被问过很多次了。我之所以再次来到这里,是因为我觉得我错过了一些简单而基本的东西。

是否有可能使这种搜索替换例程更好。例如,没有两次打开同一个文件。也欢迎速度相关的建议。

请注意,这适用于多行匹配并替换多行字符串。

#!/bin/perl -w -0777

local $/ = undef;

open INFILE, $full_file_path or die "Could not open file. $!";
$string =  <INFILE>;
close INFILE;

$string =~ s/START.*STOP/$replace_string/sm;

open OUTFILE, ">", $full_file_path or die "Could not open file. $!";
print OUTFILE ($string);
close OUTFILE;

【问题讨论】:

  • 您似乎正在尝试就地编辑文件。也就是说,打开它既可以阅读也可以写作。对吗?
  • 是的,正在编辑文件。这对我来说是最常见的用例。
  • 我倾向于通用解决方案,但有时需要提醒我,对于这样的事情,它可能(并且现在是,今天!)值得在 IDE 中尝试它(例如,IntellJ 的 Find In Project + Find-and-Replace)与加速、试错和事后检查脚本解决方案。

标签: perl


【解决方案1】:

这种搜索和替换可以用单行来完成,比如-

perl -i -pe 's/START.*STOP/replace_string/g' file_to_change

如需更多方法来完成同样的事情,请查看thread。要处理多行搜索,请使用以下命令 -

perl -i -pe 'BEGIN{undef $/;} s/START.*STOP/replace_string/smg' file_to_change

要将以下代码从单行代码转换为 perl 程序,请查看 perlrun documentation

如果你真的需要把它转换成一个工作程序,那么就让 Perl 为你处理文件的打开/关闭。

#!/usr/bin/perl -pi
#multi-line in place substitute - subs.pl
use strict;
use warnings;

BEGIN {undef $/;}

s/START.*STOP/replace_string/smg;

然后您可以使用文件名作为第一个参数调用脚本

$perl subs.pl file_to_change

如果你想要一个更丰富的脚本来处理文件打开/关闭操作(我们不喜欢所有那些“死”语句)然后看看 perlrun 中的示例 -i[extension]切换。

【讨论】:

  • 检查编辑,BEGIN 块现在确保这也适用于多行匹配。
  • 好吧,能不能写成 perl 代码(不是单行代码)?我想知道文件打开/写入例程会发生什么。
  • START 和 STOP 分别是您尝试匹配的正则表达式的开始和结束。通过取消输入记录分隔符('$/')的定义,我们有效地让 Perl 将整个文件一次性插入到 $_ 中,从而使我们能够进行多行替换。
  • 更短的版本:perl -i -p0e 's/START.*STOP/replace_string/smg' file_to_change-0 将行分隔符设置为 nul)。
  • 对于那些想知道undef $/;是什么的人。它被称为“啜饮模式”。更多信息here.
【解决方案2】:

从 cmets 中提取简短答案,供任何寻求快速单行的人使用,以及 Perl 从命令行忽略其 RegEx 选项的原因。

perl -0pe 's/search/replace/gms' file

没有-0 参数,Perl 处理数据line-by-line,这会导致多行搜索失败。

【讨论】:

  • 完美。如果它似乎不起作用,请尝试使用\R(匹配各种行尾)而不是\n
  • 对我来说,0 开关是至关重要的。谢谢和+1
  • perl -0777 -i -pe 's/search/replace/' 1.h 在 macosx 上为我工作
【解决方案3】:

考虑到您在文件的全部内容中使用:

local $/ = undef;

open INFILE, $full_file_path or die "Could not open file. $!";
$string =  <INFILE>;
close INFILE;

然后使用$string 进行所有处理,您处理文件的方式与处理内容的方式之间没有任何联系。如果在读完文件之前打开文件进行写入,则会遇到问题,因为打开文件进行写入会创建一个新文件,并丢弃以前的内容。

如果您只想保存打开/关闭语句,则使用Jonathan Leffer suggested。如果您的问题是关于多行搜索和替换,请说明问题所在。

【讨论】:

  • 这是关于通用多行搜索和替换的。即使文件非常大,我再次打开同一个文件指针真的很好吗?在单行中,似乎不需要两次打开同一个文件。我在这里仍然缺少一些东西。也许我应该在实践中看到乔纳森的例子。
  • 创建文件处理程序与文件大小无关。这只是一个指针。打开文件的行为并不意味着读取其内容。
  • 我认为这与我的误解差不多。如何在读取时为读取和写入打开同一个文件一次,这意味着通过它找到可能的匹配项的必要操作?
  • 您必须阅读它一次且仅一次。当您打开它进行写作时,您根本没有在阅读它。在您打开文件进行写入之前文件有多大并不重要,因为无论如何您都会丢弃所有这些。
【解决方案4】:

您可能想查看我的 Perl 脚本,该脚本经过实战测试(在生产中大量使用),并且具有很多功能,例如:

  • 执行多个搜索替换或查询搜索替换操作
  • 搜索替换表达式可以在命令行中给出或从文件中读取 处理多个输入文件
  • 递归地进入目录并对所有文件进行多次搜索/替换操作
  • 用户定义的 perl 表达式应用于每个输入文件的每一行 可选择以段落模式运行(用于多行搜索/替换)
  • 交互模式
  • 批处理模式
  • 可选择备份文件和备份编号
  • 以 root 身份运行时保留模式/所有者
  • 忽略符号链接、空文件、写保护文件、套接字、命名管道和目录名
  • 可选地替换仅匹配/不匹配给定正则表达式的行

https://github.com/tilo/replace_string

【讨论】:

  • -1 这不是答案,因为您没有告诉 OP 如何解决问题,而只是指出了您的代码。如果您解释了解决 OP 查询的代码的关键部分,那将是一个更好的答案。
  • @Lou 我提供了一个更通用的工具,也就是使用 Perl。你看过它的源代码吗?你可能会在那里找到答案.. ;) 鉴于有一个方便的工具可以进行多文件搜索/替换操作,最好使用它,而不是尝试“手动”编写代码
  • 如果您在答案中解释了您的一般解决方案如何解决问题,那会很好。链接(或存储库)可能会中断,那么未来的读者对于您的通用解决方案如何帮助任何人进行多行搜索+替换并不明智。另请参阅:Your answer is in another castlethis answer 类似的问题。
【解决方案5】:

bash script + perl -pi -e 的组合是无敌的——直接在EOF标签前键入搜索和替换字符串的bash函数示例:

# usage put into foobar.sh file, source foobar.sh file
# call directly into the shell do_multiline_srch_and_replace
do_multiline_srch_and_replace(){

                test -z $dir_to_work && {
         echo "You must export dir_to_work=<<the-dir>> - it is empty !!!"; exit 1;
      }
                test -d $dir_to_work || {
         echo "The dir to work on: \"$dir_to_work\" is not a dir !!!"; exit 1;
      }

                echo "INFO dir_to_work: $dir_to_work" ; sleep 1
                echo "INFO START :: searching and replacing in the non-binary files only"

                while read -r file ; do (
                        echo "DEBUG working on the following file: $file"

         # those pattern in the file names we want to skip usually - git, not , py files
         case "$file" in
            *.git*)
            continue ;;
            *node_modules*)
            continue ;;
            *.venv*)
            continue ;;
         esac
         # note the string should be exactly between the s|| and the replace str between the ||gs
         # the 'EOF' guarantees that no special chars from the shell will affect the result
                        perl -pi - <<'EOF' "$file"
BEGIN{undef $/;}
s|a multiline
string|the multiline
string to replace|gs
EOF
                );
                done < <(find $dir_to_work -type f -not -exec file {} \; | grep text | cut -d: -f1)

                echo "INFO STOP  :: search and replace in non-binary files"

}

【讨论】:

    猜你喜欢
    • 2015-08-24
    • 2021-01-31
    • 2015-02-20
    • 1970-01-01
    • 2013-05-20
    • 2018-05-26
    • 2019-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多