Заменить весь абзац другим из командной строки Linux

#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. Извините, я не уловил этого в первый раз. Добавлен другой фрагмент кода, который соответствует всему абзацу.