【问题标题】:Replace entire paragraph with another from linux command line用linux命令行中的另一个替换整个段落
【发布时间】:2011-12-18 11:59:26
【问题描述】:

我遇到的问题非常简单(或者看起来如此)。我要做的就是用另一段替换一段文本(它是标题注释)。这需要在目录层次结构(源代码树)中的不同数量的文件中发生。

要替换的段落必须完整匹配,因为存在类似的文本块。

例如

替换

// ----------
// header
// comment
// to be replaced
// ----------

// **********
// some replacement
// text
// that could have any
// format
// **********

我研究过使用 sed,据我所知,它可以处理的最多行数是 2(使用 N 命令)。

我的问题是:从 linux 命令行执行此操作的方法是什么?

编辑:

获得的解决方案:最好的解决方案是 Ikegami 的,完全命令行并且最适合我想做的事情。

我的最终解决方案需要一些调整;输入数据包含许多特殊字符,替换数据也是如此。为了解决这个问题,需要对数据进行预处理以插入适当的 \n 和转义字符。最终产品是一个带有 3 个参数的 shell 脚本;包含要搜索的文本的文件、包含要替换的文本的文件以及用于递归解析扩展名为 .cc 和 .h 的文件的文件夹。从这里进行定制相当容易。

脚本:

#!/bin/bash
if [ -z $1 ]; then
    echo 'First parameter is a path to a file that contains the excerpt to be replaced, this must be supplied'
  exit 1
fi

if [ -z $2 ]; then
    echo 'Second parameter is a path to a file contaiing the text to replace with, this must be supplied'
  exit 1
fi

if [ -z $3 ]; then
    echo 'Third parameter is the path to the folder to recursively parse and replace in'
  exit 1
fi

sed 's!\([]()|\*\$\/&[]\)!\\\1!g' $1 > temp.out
sed ':a;N;$!ba;s/\n/\\n/g' temp.out > final.out
searchString=`cat final.out`
sed 's!\([]|\[]\)!\\\1!g' $2 > replace.out
replaceString=`cat replace.out`

find $3 -regex ".*\.\(cc\|h\)" -execdir perl -i -0777pe "s{$searchString}{$replaceString}" {} +

【问题讨论】:

标签: c++ linux perl replace sed


【解决方案1】:

只要标题 cmets 是唯一分隔的(即,没有其他标题注释以 // ---------- 开头),并且替换文本是恒定的,以下 awk 脚本应该可以满足您的需要:

BEGIN { normal = 1 }

/\/\/ ----------/ {
    if (normal) {
        normal = 0;
        print "// **********";
        print "// some replacement";
        print "// text";
        print "// that could have any";
        print "// format";
        print "// **********";
    } else {
        normal = 1;
        next;
    }
}

{
    if (normal) print;
}

这会打印它看到的所有内容,直到遇到段落分隔符。当它看到第一个时,它会打印出替换段落。在它看到第二段分隔符之前,它不会打印任何内容。当它看到第二个段落分隔符时,它将再次开始正常打印下一行。

虽然您在技术上可以从命令行执行此操作,但您可能会遇到棘手的 shell 引用问题,尤其是在替换文本包含任何单引号时。将脚本放在文件中可能更容易。只需将#!/usr/bin/awk -f(或which awk 返回的任何路径)放在顶部即可。

编辑

要匹配 awk 中的多行,您需要使用 getline。也许是这样的:

/\/\/ ----------/ {
    lines[0] = "// header";
    lines[1] = "// comment";
    lines[2] = "// to be replaced";
    lines[3] = "// ----------";

    linesRead = $0 "\n";
    for (i = 0; i < 4; i++) {
         getline line;
         linesRead = linesRead line;
         if (line != lines[i]) {
             print linesRead; # print partial matches
             next;
         }
    }

    # print the replacement paragraph here
    next;
}

【讨论】:

  • 为了澄清,不幸的是我想完全匹配替换文本。除非整个段落匹配,否则不应发生匹配。要替换的段落的某些部分出现在其他地方,特别是标题分隔符。
  • 抱歉,我第一次没听懂。添加了与整个段落匹配的不同代码。
【解决方案2】:

使用 perl:

#!/usr/bin/env perl
# script.pl
use strict;
use warnings;
use Inline::Files;

my $lines = join '', <STDIN>; # read stdin
my $repl = join '', <REPL>; # read replacement
my $src = join '', <SRC>; # read source
chomp $repl; # remove trailing \n from $repl
chomp $src; # id. for $src
$lines =~ s@$src@$repl@gm; # global multiline replace 
print $lines; # print output

__SRC__
// ----------
// header
// comment
// to be replaced
// ----------
__REPL__
// **********
// some replacement
// text
// that could have any
// format
// **********

用法: ./script.pl &lt; yourfile.cpp &gt; output.cpp

要求:Inline::Files(从 cpan 安装)

测试于: perl v5.12.4, Linux _ 3.0.0-12-generic #20-Ubuntu SMP Fri Oct 7 14:56:25 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux

【讨论】:

    【解决方案3】:
    find -name '*.pm' -exec perl -i~ -0777pe'
        s{// ----------\n// header\n// comment\n// to be replaced\n// ----------\n}
         {// **********\n// some replacement\n// text\n// that could have any\n// format\n// **********\n};
    ' {} +
    

    【讨论】:

      【解决方案4】:

      这可能有效:

      # cat <<! | sed ':a;N;s/this\nand\nthis\n/something\nelse\n/;ba'
      > a
      > b
      > c
      > this
      > and
      > this
      > d
      > e
      > this
      > not
      > this
      > f
      > g
      > !
      a
      b
      c 
      something
      else
      d
      e
      this
      not
      this 
      f
      g
      

      诀窍是使用N 和循环:a;...;ba 将所有内容放入模式空间 这可能更有效:

      sed '1{h;d};H;$!d;x;s/this\nand\nthis\n/something\nelse\n/g;p;d'
      

      更通用的解决方案可以使用文件来匹配和替换数据,如下所示:

      match=$(sed ':a;N;${s/\n/\\n/g};ba;' match_file)
      substitute=$(sed ':a;N;${s/\n/\\n/g};ba;' substitute_file)
      sed '1{h;d};H;$!d;x;s/'"$match"'/'"$substitute"'/g;p;d' source_file
      

      另一种方式(可能效率较低)但看起来更干净:

      sed -s '$s/$/\n@@@/' match_file substitute_file | 
      sed -r '1{h;d};H;${x;:a;s/^((.*)@@@\n(.*)@@@\n(.*))\2/\1\3/;ta;s/(.*@@@\n){2}//;p};d' - source_file
      

      最后一个使用 GNU sed --separate 选项将每个文件视为一个单独的实体。第二个 sed 命令使用循环来代替 .* 贪婪。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-26
        • 2016-05-20
        • 2010-11-17
        • 1970-01-01
        • 1970-01-01
        • 2013-08-02
        • 1970-01-01
        相关资源
        最近更新 更多