用linux命令行替换整个段落

时间:2011-10-31 04:32:19

标签: c++ linux perl replace sed

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

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

e.g。

替换

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

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

我已经看过使用sed,从我可以看出它可以处理的最多行数是2(使用N命令)。

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

编辑:

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

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

SCRIPT:

#!/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}" {} +

4 个答案:

答案 0 :(得分:7)

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};
' {} +

答案 1 :(得分:1)

使用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 < yourfile.cpp > 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

答案 2 :(得分:0)

只要标题注释被唯一分隔(即,没有其他标题注释以// ----------开头),并且替换文本是常量,以下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;
}

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

虽然从技术上讲,您可以从命令行执行此操作,但您可能会遇到棘手的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;
}

答案 3 :(得分:0)

这可能有效:

# 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命令使用循环替换来消除.*贪婪。