用Perl代替多行搜索

mlnl4t2r  于 2023-03-30  发布在  Perl
关注(0)|答案(6)|浏览(145)

我知道这样的问题以前已经被问过很多次了,我之所以再次来到这里,是因为我觉得我错过了一些简单而根本的东西。
是否有可能使这种搜索-替换例程更好。例如,不打开同一个文件两次。也欢迎速度相关的建议。
请注意,这适用于多行匹配,也可以替换多行字符串。

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

q3aa05251#

这种搜索和替换可以用一行程序来完成,例如-

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

如果你想要一个更丰富的脚本来处理文件的打开/关闭操作(我们不是喜欢所有那些'die'语句吗),那么看看perlrun中-i[extension]开关下的例子。

tkclm6bt

tkclm6bt2#

从评论中提取简短的答案,对于任何寻找快速一行程序的人,以及Perl从命令行忽略RegEx选项的原因。
perl -0pe 's/search/replace/gms' file
如果没有-0参数,Perl将处理数据line-by-line,这将导致多行搜索失败。

cgfeq70w

cgfeq70w3#

考虑到你在文件的整个内容中发出声音:

local $/ = undef;

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

然后用$string做所有的处理,你如何处理文件和你如何处理内容之间没有联系。如果你在阅读完文件之前打开文件进行写操作,你会有一个问题,因为打开一个文件进行写操作会创建一个新文件,丢弃以前的内容。
如果你想做的只是保存open/close语句,那么按照Jonathan Leffer的建议去做。如果你的问题是关于多行搜索和替换,那么请澄清问题所在。

zpgglvta

zpgglvta4#

你可能想看看我的Perl脚本,它是经过战斗测试的(在生产中大量使用),并且有很多功能,例如:

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

https://github.com/tilo/replace_string

fquxozlt

fquxozlt5#

bash脚本+ perl -pi -e的组合是无与伦比的-一个bash函数的例子,可以直接在EOF标签之前输入搜索和替换字符串:

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

}
wz8daaqr

wz8daaqr6#

我知道这个问题已经得到了回答,但这就是我解决这个问题的方法。
假设你想改变一个UUID,但是上面的行必须有一个匹配,因为你有很多属于其他东西的UUID。
Ubuntu 20中的bash脚本中的perl调用:

_UUID=$(uuidgen | sed 's/-//g')
export _UUID
perl -0777 -pi.back -e 's/(<stringProp\sname="Argument\.name">_BINARYVIDEOTEMPURL<\/stringProp>\n.*<stringProp\sname="Argument\.value">)[a-zA-Z0-9]{32}(<\/stringProp>)/$1$ENV{_UUID}$2/g;' test.txt

test.txt文件如下所示:(我知道不是有效的XML,但只需创建它)

<?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.2.1">
  <hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="K8S Load Test Plan" enabled="true">      
  <stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
      <collectionProp name="Arguments.arguments">
        <elementProp name="_SESSIONID" elementType="Argument">
          <stringProp name="Argument.name">_SESSIONID</stringProp>
          <stringProp name="Argument.value">7c096b65-84b6-40c9-be93-a5891ec0394d</stringProp>
          <stringProp name="Argument.metadata">=</stringProp>
        </elementProp>
        <elementProp name="_BINARYVIDEOTEMPURL" elementType="Argument">
          <stringProp name="Argument.name">_BINARYVIDEOTEMPURL</stringProp>
          <stringProp name="Argument.value">64e1886127fa41c4a58e59fe2bb098e1</stringProp>
          <stringProp name="Argument.metadata">=</stringProp>
        </elementProp>
      </collectionProp>

这里发生了很多事情,但让我解释一下。
1.创建一个新的UUID来替换。
1.导出UUID,因为perl将在ENVIRONMENT变量中获取它。
1.调用Perl来处理搜索和替换

  • -077使perl能够使用多行并完成多行的前向和后向。我不能告诉你perl是如何工作的。
  • -pi.back基本上是内联编辑和备份文件。
  • -e基本上是's/reaplcethis/withthis/g',但它包含了需要匹配的新行的正则表达式。另外,它展示了如何使用环境变量和分组来重新创建字符串。

不管怎样,希望这对某人有帮助。

相关问题