2011-10-31 42 views
4

我拥有的问题非常简单(或者看起来如此)。我想要做的就是用另一段替换一段文本(它是一个标题注释)。这将需要通过目录层次结构(源代码树)中的各种文件进行。从linux命令行中用另一个替换整个段落

由于存在相似的文本块,所以要替换的段落必须完全匹配。

例如

要更换

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

随着

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

我已经看过用SED,从我可以告诉数量最多的,它可以工作线是2(用N命令)。

我的问题是:如何从linux命令行执行此操作?

编辑:获得

解决方法:最好的解决办法是池上的,全面的命令行和最适合什么,我想做的事。

我的最终解决方案需要一些调整;输入数据与替换数据一样包含许多特殊字符。为了解决这个问题,需要预先处理数据以插入适当的\ 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}" {} + 
+0

难道你不能只使用sed,包括正则表达式中的换行符吗? – wim

+0

我试过了,发现这个:http://backreference.org/2009/12/23/how-to-match-newlines-in-sed/ – radman

回答

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

这会打印看到的所有内容,直到运行到段落分隔符为止。当它看到第一个时,它会打印出替换段落。直到看到第二段分隔符,它才会打印任何内容。当它看到第二段分隔符时,它将通过下一行再次开始正常打印行。

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

为了澄清,不幸的是我想要完全匹配替换文本。除非整个段落匹配,否则不应发生匹配。要替换的段落的某些部分出现在别处,特别是标题分隔符。 – radman

+0

对不起,我第一次没有接受。添加了与整个段落相匹配的不同代码。 –

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 =~ [email protected][email protected][email protected]; # 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泛型#20,Ubuntu的SMP周五10月7日14点56分25秒UTC 2011 x86_64的x86_64的x86_64的GNU/Linux的

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/$/\[email protected]@@/' 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命令使用替代循环来避免.*贪婪。