我有的问题是相当简单的(或者看起来如此)。 我想要做的就是用另一段replace一段文本(这是一个标题注释)。 这将需要发生在目录层次结构(源代码树)中的各种文件中。
由于存在相似的文本块,所以要replace的段落必须完全匹配。
例如
取代
// ---------- // header // comment // to be replaced // ----------
同
// ********** // some replacement // text // that could have any // format // **********
我已经看过使用sed和我能告诉它可以工作的最多的行数是2(使用N命令)。
我的问题是:什么是从Linux命令行做到这一点?
编辑:
解决scheme:最好的解决scheme是池上的,完全的命令行,最适合我想做的事情。
我的最终解决scheme需要一些调整; input数据包含很多特殊字符,replace数据也是如此。 为了解决这个问题,需要预处理数据以插入适当的\ n和转义字符。 最终产品是一个需要3个参数的shell脚本; 包含要search的文本的文件,包含要replace的文本的文件以及recursionparsing具有.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}" {} +
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}; ' {} +
使用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
要求: 内联::文件 (从cpan安装)
测试: perl v5.12.4,Linux _ 3.0.0-12-generic#20-Ubuntu SMP Fri Oct 10 7:56:25 UTC 2011 x86_64 x86_64 x86_64 GNU / Linux
只要标题注释是唯一分隔的(即,没有其他头文件注释以// ----------
开头),并且替换文本是不变的,下面的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; }
这可能工作:
# 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命令使用替代的循环来消除.*
贪婪。