Я хотел бы вставить новую строку в последней строке, начиная с некоторого текста, используя 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

8 ответов

Я предполагаю, что ваш пример ввода - это файл с именем 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, за которым следует строка break (\n) или конец файла ($).

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

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

2
ответ дан 18 July 2018 в 13:01

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

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
ответ дан 18 July 2018 в 13:01

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

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

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

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

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

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

2
ответ дан 18 July 2018 в 13:01

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

sed + tac (с двойным вводом) AWK с двойным входом Perl с двойным входом Альтернативный 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

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

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

AWK

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 дважды - сначала, чтобы найти, где находится последнее 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

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

То, что здесь также отличается, это то, что с -s switch perl позволяет передавать аргументы. В этом случае строка, которую мы хотим вставить, передается через переключатель -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
ответ дан 18 July 2018 в 13:01

Я предполагаю, что ваш пример ввода - это файл с именем 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, за которым следует строка break (\n) или конец файла ($).

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

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

2
ответ дан 24 July 2018 в 20:05
  • 1
    Эта работа выполняется только в том случае, если Apple - последняя строка. Это справедливо для примера, но, вероятно, не для каждого возможного файла. – Philippos 20 May 2017 в 14:59
  • 2
    @Philippos Я добавил способ вставки строки после последнего появления «Apple». Если бы вы были тем, кто занижен, подумайте о том, чтобы отказаться от этого, основываясь на текущем состоянии моего ответа. Благодарю. – Byte Commander 20 May 2017 в 15:26
  • 3
    Я не тестировал, но если ключевое слово было в последней строке, это было бы добавлено после следующего-последнего появления, правильно? Но я не испытываю -z. И задняя часть лишняя, так как вам не нужно ее заменять, вы можете ее оставить. – Philippos 20 May 2017 в 15:41
  • 4
    Вы правы, спасибо за предложения. Я немного изменил команду. – Byte Commander 20 May 2017 в 16:03
  • 5
    Nitpicking Я мог заметить, что это добавляет нежелательную новую строку в конец файла, если совпадение находится в последней строке. (-; – Philippos 21 May 2017 в 07:02

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

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
ответ дан 24 July 2018 в 20:05
  • 1
    Вы пропустили, что строка не всегда должна быть добавлена ​​в конец файла, но после последнего вхождения слова apple – Philippos 20 May 2017 в 17:29
  • 2
    @Philippos Ответ дает решение для соответствия примеру, предоставленному OP. Опять же, как я уже упоминал в другом комментарии, ответы соответствуют тому, что задают. Во-вторых, я не вижу причин, по которым вы просите об этом, если бы сам ОП не опубликовал это как требование. – Sergiy Kolodyazhnyy 21 May 2017 в 06:18
  • 3
    Это часть его вопроса («после последнего яблока») и даже в названии. Я согласен с тем, что он должен был представить несколько примеров, но вы не можете полагаться только на примеры. Очень часто было бы легко найти решение только для примера, которое не удастся для многих других. Часто помогает чтение вопроса. – Philippos 21 May 2017 в 06:48
  • 4
    Пожалуйста, можете ли вы сказать что-то OP, какую часть вопроса я должен читать? – George Udosen 21 May 2017 в 07:00
  • 5
    @Philippos Опять же, если только ОП не заявляет об этом, это не требование, и поэтому не является основанием для наложения таких требований на других людей, отвечая на вопрос. – Sergiy Kolodyazhnyy 21 May 2017 в 07:14

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

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

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

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

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

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

2
ответ дан 24 July 2018 в 20:05

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

sed + tac (с двойным вводом) AWK с двойным входом Perl с двойным входом Альтернативный 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

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

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

AWK

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 дважды - сначала, чтобы найти, где находится последнее 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

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

То, что здесь также отличается, это то, что с -s switch perl позволяет передавать аргументы. В этом случае строка, которую мы хотим вставить, передается через переключатель -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
ответ дан 24 July 2018 в 20:05
  • 1
    «Выполнение этого с помощью sed может быть очень сложным. Awk может быть немного friendliner для того, что вы пытаетесь сделать & quot; - глядя на мое (и другое) решение sed, которое почти наполовину длиннее и загадочно, как ваша команда awk ... ;-) – Byte Commander 21 May 2017 в 00:50
  • 2
    @ByteCommander ОК, это дано, мое решение может быть более длинным, но оно работает;) – Sergiy Kolodyazhnyy 21 May 2017 в 01:13
  • 3
    Фактически, ваше решение sed вставляет перед последним apple, но вы можете легко его изменить. Я не вижу причин добавлять длинные и загадочные решения, когда уже даны простые. – Philippos 21 May 2017 в 06:57
  • 4
    @Philippos Спасибо, что указали, что уже исправлено. Решения не являются более загадочными, чем sed 'regex, и являются более процедурными, плюс я объяснил, как каждое решение работает в принципе. Я полагаю, что дело вкуса и знакомства с инструментами. Опять же, длина также не имеет значения. Если он работает, он работает. Здесь нас не интересует кодекс-гольф. – Sergiy Kolodyazhnyy 21 May 2017 в 07:10

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

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