Я хотел бы вставить новую строку в последнюю строку, начинающуюся с некоторого текста, используя sed, [closed]

Пример:

Вход:

Apple
Banana
Apple
Orange
Apple

Выход:

Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
2
задан 20 May 2017 в 14:38

4 ответа

Я видел этот пример здесь

sed -i -e "\$aTEXTTOEND" <filename>

Информация:

i: edit files in place    
e: add the script to the commands to be executed    
$: sed address location
a: append command
TEXTTOEND: text to append to end of file
0
ответ дан 20 May 2017 в 14:38

С помощью GNU sed вы можете сделать это:

sed ':a;N;$! ba;s/.*\nApple/&\nYour appended line/'

Шаблон :a;N;$! ba добавляет строки из файла в буфер с N, в то время как конец файла (адрес $) ) не достигнут (!) (переходите к :a с помощью ba). Таким образом, если вы выходите из цикла, все строки находятся в буфере, и шаблон поиска .*\nApple соответствует всему файлу до последней строки, начиная с Apple. & в строке замены вставляет все совпадение и добавляет ваш текст.

Переносимое решение, работающее также и на других sed версиях, будет

sed -e ':a' -e 'N;$! ba' -e 's/.*\(\n\)Apple/&\1Your appended line/'

Здесь сценарий разбит по меткам и вместо использования \n в строке замены (что не гарантируется) для работы с не-GNU sed s) мы ссылаемся на образец из шаблона, окруженного \(\) ссылкой \1.

0
ответ дан 20 May 2017 в 14:38

Я предполагаю, что ваш пример ввода - это файл с именем fruits.txt.


Если вы хотите вставить строку после последнего появления «Apple», это можно сделать с помощью sed следующим образом:

sed -rz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'

Опция -r включает расширенный регулярный выражения.
Опция -z говорит sed принимать NUL-символы (код ASCII 0) в качестве разделителей строк вместо обычных символов новой строки. Таким образом, весь текстовый файл будет обрабатываться сразу, а не построчно.

Команда sed s/PATTERN/REPLACEMENT/ ищет (расширенное из-за -r) регулярное выражение в PATTERN и заменяет его оцененной строкой REPLACEMENT.

Регулярное выражение (^(.*\n)?Apple)(\n|$) соответствует любому количеству строк от начала (^) до последнего вхождения Apple, за которым следует разрыв строки (\n) или конец файла ($ ).

Теперь все это совпадение заменяется на \1\ninserted line\n, где \1 обозначает исходное содержимое первой группы совпадений, то есть все до Apple. Так что на самом деле он вставляет inserted line перед и после него разрыв строки (\n) сразу после последнего появления «Apple».

Это также работает, если строка «Apple» является первой, последней или единственной строкой в ​​файле.

Пример входного файла (добавьте одну строку в ваш пример, чтобы прояснить поведение):

Apple
Banana
Apple
Orange
Apple
Cherry

Пример вывода:

Apple
Banana
Apple
Orange
Apple
inserted line
Cherry

Если вы довольны результатом и Если вы хотите отредактировать fruits.txt на месте, вместо того, чтобы просто выводить модификацию, вы можете добавить опцию -i:

sed -irz 's/(^(.*\n)?Apple)(\n|$)/\1\ninserted line\n/'

Если вы просто хотите добавить строку текста в конец из этого файла, хотя sed не является оптимальным инструментом.

Вы можете просто использовать добавленное перенаправление вывода Bash >>, затем:

echo "I am new string replaced at last apple" >> fruits.txt

>> будет принимать стандартный вывод команды слева (команда echo здесь просто выводит свои аргументы в стандартный вывод) и добавляет его в указанный файл. Если файл еще не существует, он будет создан.

0
ответ дан 20 May 2017 в 14:38

В этом ответе:

  1. sed+tac (удваивают входное реверсирование),
  2. AWK с двойным входом
  3. Perl с двойным входом
  4. Альтернативный Perl с двойным входным реверсированием

sed + tac

Этот подход использует 3 процесса, сначала инвертируя файл, с помощью sed для нахождения первого соответствия и вставляя требуемый текст после первого соответствия, и инвертируя текст снова.

$ tac input.txt | sed '0,/Apple/{s/Apple/NEW THING\n&/}' | tac 
Apple
Banana
Apple
Orange
Apple
NEW THING
something else
something other entirely
blah

Основная идея совпадает с альтернативной версией жемчуга, записанной ниже: первое соответствие в обратном списке строк, последнее соответствие в исходном списке. Преимуществом этого подхода является простота и удобочитаемость, что касается пользователя.

Этот подход будет работать хорошо на маленькие файлы, но это является громоздким для больших файлов и вероятно займет большое количество времени для обработки большой суммы строк.

AWK

Awk может быть небольшим friendliner для того, что Вы пытаетесь сделать:

$ awk -v n="AFTER LAST APPLE" 'NR==FNR&&/Apple/{l=NR};NR!=FNR{if(FNR==l){print $0;print n}else print}' input.txt input.t>
Apple
Banana
Apple
Orange
Apple
AFTER LAST APPLE
something else
something other entirely
blah

Основная идея здесь состоит в том, чтобы дать Ваш входной файл awk дважды - сначала для нахождения, где последняя Apple, и вторая для вставки текста, когда мы передаем положение того последнего "яблока", которое мы нашли от первого повторения.

Ключ к созданию этой работы должен оценить NR (который продолжает считать все обработанные строки, общие количества), и FNR (номер строки в текущем файле) переменные. Когда эти два файла неравны, мы знаем, что обрабатываем второй файл, таким образом мы знаем, что сделали нашу первичную обработку из нахождения местоположения последнего вхождения Apple.

Так как мы читаем файл дважды, производительность будет зависеть от размера файла, но это лучше, чем sed + tac комбинация, так как мы не должны инвертировать файл дважды.

Perl

perl -sne '$t+=1;$l=$. if /Apple/ and $.==$t;
          if($t!=$.){if($.==$l){print $_ . $n . "\n";}else{print $_}};
          close ARGV if eof;' -- -n="AFTER LAST APPLE" input.txt input.txt

Решение для жемчуга следует за той же идеей как AWK один. Мы передаем вход дважды и используем первый раз, когда файл передается для нахождения, где последнее вхождение Apple, и сохраните ее номер строки в $l переменная. То, что отличается от awk здесь, то, что существует нет FNR переменная в жемчуге, следовательно мы должны использовать $t+=1 и close ARGV if eof с этой целью.

Что также отличается, вот это с -s жемчуг переключателя позволяет Вам передавать аргументы. В этом случае строка, которую мы хотим вставить, передается через -n переключатель.

Альтернативный Perl

Вот еще один способ сделать это в Perl. Идея состоит в том, чтобы просто считать файл в массив строк и реверс тот массив. Первое соответствие в обратном массиве является последним в исходном списке строк. Таким образом мы можем вставить требуемый текст перед той строкой и инвертировать список снова:

$ perl -le '@a1=<>;END{do{push @a2,"NEW LINE\n" and $f=1 if $_ =~ /Apple/ and !$f; push @a2,$_} for reverse(@a1); print reverse(@a2)}' input.txt      
Apple
Banana
Apple
Orange
Apple
NEW LINE
something else
something other entirely
blah
2
ответ дан 20 May 2017 в 14:38

Другие вопросы по тегам:

Похожие вопросы: