我遇到的问题非常简单(或者看起来如此)。我想要做的就是用另一个段落替换一段文本(它是标题注释)。这需要在目录层次结构(源代码树)中的各种文件中发生。
要替换的段落必须完整匹配,因为存在类似的文本块。
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}" {} +
答案 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命令使用循环替换来消除.*
贪婪。