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

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

Найдите в файле последнюю строку с заданной строкой, чтобы добавить строку после этого совпадения. Пример:

Вход:

Apple
Banana
Apple
Orange
Apple
Grapefruit

Выход:

Apple
Banana
Apple
Orange
Apple
I am new string replaced at last apple
Grapefruit

решение1

С помощью 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 seds) мы ссылаемся на ту из шаблона, окруженную \(\)ссылкой \1.

решение2

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

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

решение3

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


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

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

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

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

Регулярное выражение (^(.*\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здесь команда просто печатает свои аргументы в стандартный вывод) и добавлять его в файл, указанный справа. Если файл еще не существует, он будет создан.

решение4

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

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

СЭД + ТАК

В этом подходе используются 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

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

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

АВК

Awk может быть немного удобнее для того, что вы пытаетесь сделать:

$ 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 — сначала для поиска последнего яблока, а затем для вставки текста при прохождении позиции последнего «яблока», найденного нами в первой итерации.

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

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

Перл

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

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

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

Альтернативный 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

Связанный контент