sed заменил шаблон соответствия последней строки
12186 просмотра
14 ответа
Учитывая файл как это:
a
b
a
b
Я хотел бы иметь возможность sed
заменить только последнюю строку, которая содержит экземпляр «а» в файле. Так что, если я хочу заменить его на «c», то результат должен выглядеть следующим образом:
a
b
c
b
Обратите внимание, что мне нужно, чтобы это работало независимо от того, сколько совпадений он может встретить, или от точных деталей того, каким может быть желаемый шаблон или содержимое файла. Заранее спасибо.
Автор: TTT Источник Размещён: 13.11.2019 11:32Ответы (14)
12 плюса
Не совсем седь только
tac file | sed '/a/ {s//c/; :loop; n; b loop}' | tac
тестирование
% printf "%s\n" a b a b a b | tac | sed '/a/ {s//c/; :loop; n; b loop}' | tac
a
b
a
b
c
b
Обратитесь к файлу, затем для первого соответствия сделайте подстановку и затем безоговорочно выплесните остальную часть файла. Затем снова переверните файл.
Обратите внимание, что пустое регулярное выражение (здесь как s//c/
) означает повторное использование предыдущего регулярного выражения ( /a/
)
Я не большой поклонник sed, кроме очень простых программ. Я бы использовал awk:
tac file | awk '/a/ && !seen {sub(/a/, "c"); seen=1} 1' | tac
Автор: glenn jackman
Размещён: 14.06.2013 07:02
4 плюса
Здесь много хороших ответов; вот концептуально простой двухпроходная sed
решение помогает , tail
что POSIX-совместимых и не читает весь файл в память, подобный подход Эран Бен-Натана :
sed "$(sed -n '/a/ =' file | tail -n 1)"' s/a/c/' file
sed -n '/a/=' file
выводит номера строк (функции=
), соответствующих регулярному выражениюa
, иtail -n 1
извлекает последнюю строку вывода , то есть номер строки в файле,file
содержащей последнее вхождение регулярного выражения.Помещение подстановки команды
$(sed -n '/a/=' file | tail -n 1)
непосредственно перед' s/a/c'
результатом приводит к внешнемуsed
сценарию, такому как3 s/a/c/
(с вводом примера), который выполняет желаемую подстановку только для последнего, с которым произошло регулярное выражение.
Если шаблон не найден во входном файле, вся команда является эффективной командой no-op.
Автор: mklement0 Размещён: 25.01.2017 06:124 плюса
Это может работать для вас (GNU sed):
sed -r '/^PATTERN/!b;:a;$!{N;/^(.*)\n(PATTERN.*)/{h;s//\1/p;g;s//\2/};ba};s/^PATTERN/REPLACEMENT/' file
или другим способом:
sed '/^PATTERN/{x;/./p;x;h;$ba;d};x;/./{x;H;$ba;d};x;b;:a;x;/./{s/^PATTERN/REPLACEMENT/p;d};x' file
или если вам нравится:
sed -r ':a;$!{N;ba};s/^(.*\n?)PATTERN/\1REPLACEMENT/' file
Если подумать, это решение может заменить первые два:
sed '/a/,$!b;/a/{x;/./p;x;h};/a/!H;$!d;x;s/^a$/c/M' file
Если регулярное выражение не где найти в файле, файл будет проходить без изменений. Как только регулярное выражение совпадет, все строки будут сохранены в области удержания и будут напечатаны при выполнении одного или обоих условий. Если последующее регулярное выражение встречается, содержимое области удержания печатается, и последнее регулярное выражение заменяет его. В конце файла первая строка пространства удержания будет содержать последнее соответствующее регулярное выражение, и это можно заменить.
Автор: potong Размещён: 15.06.2013 07:183 плюса
Другой:
tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
В бою:
$ printf "%s\n" a b a b a b | tr '\n' ' ' | sed 's/\(.*\)a/\1c/' | tr ' ' '\n'
a
b
a
b
c
b
Автор: Fredrik Pihl
Размещён: 14.06.2013 07:31
3 плюса
Другой подход:
sed "`grep -n '^a$' a | cut -d \: -f 1 | tail -1`s/a/c/" a
Преимущество этого подхода заключается в том, что вы дважды запускаете файл и не читаете его в память. Это может иметь смысл в больших файлах.
Автор: Eran Ben-Natan Размещён: 17.06.2013 08:002 плюса
Здесь все сделано в одном awk
awk 'FNR==NR {if ($0~/a/) f=NR;next} FNR==f {$0="c"} 1' file file
a
b
c
b
Это читает файл дважды. Первый запуск, чтобы найти последний a
, второй запуск, чтобы изменить его.
2 плюса
Двухпроходное решение для буферизации всего ввода недопустимо:
sed "$(sed -n /a/= <em>file</em> | sed -n '$s/$/ s,a,c,/p' )" <em>file</em>
(в более ранней версии этой ошибки возникла ошибка с расширением истории при установке redhat bash-4.1, таким образом можно избежать $!d
ошибочного расширения.)
Однопроходное решение, которое буферизует как можно меньше:
sed '/a/!{1h;1!H};/a/{x;1!p};$!d;g;s/a/c/'
Простейшие:
tac | sed '0,/a/ s/a/c/' | tac
Автор: jthill
Размещён: 17.06.2013 07:36
1 плюс
tac infile.txt | sed "s/a/c/; ta ; b ; :a ; N ; ba" | tac
Первая tac
переворачивает строки infile.txt
, sed
выражение (см. Https://stackoverflow.com/a/9149155/2467140 ) заменяет первое совпадение «a» на «c» и печатает оставшиеся строки, а последняя tac
переворачивает строки обратно в их первоначальном порядке.
1 плюс
Вот способ только с использованием awk
:
awk '{a[NR]=$1}END{x=NR;cnt=1;while(x>0){a[x]=((a[x]=="a"&&--cnt==0)?"c <===":a[x]);x--};for(i=1;i<=NR;i++)print a[i]}' file
$ cat f
a
b
a
b
f
s
f
e
a
v
$ awk '{a[NR]=$1}END{x=NR;cnt=1;while(x>0){a[x]=((a[x]=="a"&&--cnt==0)?"c <===":a[x]);x--};for(i=1;i<=NR;i++)print a[i]}' f
a
b
a
b
f
s
f
e
c <===
v
Автор: jaypal singh
Размещён: 14.06.2013 07:56
1 плюс
Это также можно сделать в perl:
perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp > your_new_file
Проверено:
> cat temp
a
b
c
a
b
> perl -e '@a=reverse<>;END{for(@a){if(/a/){s/a/c/;last}}print reverse @a}' temp
a
b
c
c
b
>
Автор: Vijay
Размещён: 17.06.2013 11:35
0 плюса
Вот еще один вариант:
sed -e '$ a a' -e '$ d' file
Первая команда добавляет, a
а вторая удаляет последнюю строку. Со sed(1)
страницы руководства :
Автор: Carl Norum Размещён: 14.06.2013 06:21
$
Соответствуйте последней строке.
d
Удалить шаблон пространства. Начать следующий цикл.
a text
Добавить текст, в котором каждой новой строке вставлена обратная косая черта.
0 плюса
Вот команда:
sed '$s/.*/a/' filename.txt
И вот оно в действии:
> echo "a
> b
> a
> b" > /tmp/file.txt
> sed '$s/.*/a/' /tmp/file.txt
a
b
a
a
Автор: Thomas Kelley
Размещён: 14.06.2013 06:19
0 плюса
awk
-только решение:
awk '/a/{printf "%s", all; all=$0"\n"; next}{all=all $0"\n"} END {sub(/^[^\n]*/,"c",all); printf "%s", all}' file
Объяснение:
- Когда строка соответствует
a
, все строки между предыдущимa
(не включая) текущимa
(т.е. содержимым, хранящимся в переменнойall
), печатаются - Когда строка не совпадает
a
, она добавляется к переменнойall
. - Соответствие последней строки
a
не сможетall
распечатать содержимое, поэтому вы вручную распечатываете его вEND
блоке. Однако до этого вы можете заменить соответствие строкиa
любым, что пожелаете.
0 плюса
Дано:
$ cat file
a
b
a
b
Вы можете использовать POSIX grep
для подсчета совпадений:
$ grep -c '^a' file
2
Затем введите этот номер awk
для печати замены:
$ awk -v last=$(grep -c '^a' file) '/^a/ && ++cnt==last{ print "c"; next } 1' file
a
b
c
b
Автор: dawg
Размещён: 25.01.2017 04:49
Вопросы из категории :
- regex Learning Regular Expressions
- regex Regex и unicode
- regex Мое регулярное выражение слишком подходит. Как мне это остановить?
- regex Как выполнить подстановку Perl для строки, сохранив оригинал?
- regex Замена n-го экземпляра совпадения с регулярным выражением в Javascript
- regex Как заменить простые URL ссылками?
- bash Как разрешить символические ссылки в сценарии оболочки
- bash Удалить все, кроме самых последних файлов X в Bash
- bash Распараллелить скрипт Bash с максимальным количеством процессов
- bash Как мне написать цикл для Bash
- bash Как обнулить числа в именах файлов в Bash?
- bash Get the source directory of a Bash script from within the script itself
- sed Как я могу извлечь предопределенный диапазон строк из текстового файла в Unix?
- sed Как удалить первую строку текстового файла с помощью сценария bash / sed?
- sed Избегайте строки для шаблона замены sed
- sed Используя sed, как вы печатаете первые 'N' символов строки?
- sed upper- to lower-case using sed
- sed sed поиск и замена всего слова