#c #linux #perl #replace #sed
#c #linux #perl #заменить #sed
Вопрос:
Проблема, с которой я столкнулся, довольно проста (или так кажется). Все, что я хочу сделать, это заменить абзац текста (это комментарий заголовка) другим абзацем. Это должно произойти для разного количества файлов в иерархии каталогов (дерево исходного кода).
Заменяемый абзац должен совпадать полностью, поскольку существуют похожие текстовые блоки.
например
Для замены
// ----------
// header
// comment
// to be replaced
// ----------
С помощью
// **********
// some replacement
// text
// that could have any
// format
// **********
Я рассмотрел использование sed и, насколько я могу судить, наибольшее количество строк, с которыми он может работать, равно 2 (с помощью команды N).
Мой вопрос: как это сделать из командной строки Linux?
Редактировать:
Полученное решение: лучшим решением было Ikegami, полностью из командной строки и наилучшим образом подходит для того, что я хотел сделать.
Мое окончательное решение потребовало некоторой настройки; входные данные содержали много специальных символов, как и данные замены. Чтобы справиться с этим, данные должны быть предварительно обработаны для вставки соответствующих символов n и escape. Конечный продукт представляет собой сценарий оболочки, который принимает 3 аргумента; Файл, содержащий текст для поиска, файл, содержащий текст для замены, и папку для рекурсивного анализа файлов с расширением .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!([]()|*$/amp;[])!\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}" {}
Комментарии:
1. Разве вы не можете просто использовать sed, включая новые строки в вашем регулярном выражении?
2. Я попробовал это, нашел это: backreference.org/2009/12/23/how-to-match-newlines-in-sed
Ответ №1:
find -name '*.pm' -exec perl -i~ -0777pe'
s{// ----------n// headern// commentn// to be replacedn// ----------n}
{// **********n// some replacementn// textn// that could have anyn// formatn// **********n};
' {}
Ответ №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 < yourfile.cpp > output.cpp
Требования к Inline::Files : (установка из cpan)
Протестировано на: perl v5.12.4, Linux _ 3.0.0-12-generic # 20-Ubuntu SMP Пт 7 октября 14:56:25 UTC 2011 x86_64 x86_64 x86_64 GNU / Linux
Ответ №3:
Это может сработать:
# cat <<! | sed ':a;N;s/thisnandnthisn/somethingnelsen/;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/thisnandnthisn/somethingnelsen/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/13/;ta;s/(.*@@@n){2}//;p};d' - source_file
Последний использует опцию GNU sed --separate
для обработки каждого файла как отдельного объекта. Вторая команда sed использует цикл для замены, чтобы избежать .*
жадности.
Ответ №4:
Пока комментарии заголовка разделены однозначно (т. Е. Ни один другой комментарий заголовка не начинается с // ----------
), а текст замены является постоянным, следующий 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-го абзаца, он снова начнет печатать строки в обычном режиме со следующей строки.
Хотя технически вы можете сделать это из командной строки, вы можете столкнуться с проблемами кавычек в командной строке, особенно если заменяющий текст содержит одинарные кавычки. Может быть проще поместить скрипт в файл. Просто поместите #!/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;
}
Комментарии:
1. Чтобы уточнить, к сожалению, я хочу точно соответствовать тексту замены. Совпадение не должно происходить, если не совпадает весь абзац. Некоторые части заменяемого абзаца отображаются в другом месте, в частности разделитель заголовка.
2. Извините, я не уловил этого в первый раз. Добавлен другой фрагмент кода, который соответствует всему абзацу.