Я хотел бы обновить большое количество исходных файлов C ++ с помощью дополнительной директивы include перед любым существующим #include. Для такого рода задач я обычно использую небольшой скрипт bash с sed, чтобы переписать файл.
Как мне sed
заменить только первое вхождение строки в файле, а не заменять каждое вхождение?
Если я использую
sed s/#include/#include "newfile.h"\n#include/
он заменяет все #include.
Альтернативные предложения для достижения того же самого также приветствуются.
command-line
sed
text-processing
Дэвид Диббен
источник
источник
0,
работает только сgnu sed
s//
- то есть пустое регулярное выражение - означает, что последнее примененное регулярное выражение неявно используется повторно; в этом случаеRE
. Этот удобный ярлык означает, что вам не нужно дублировать регулярное выражение в концеs
вызова.sed
Скрипт , который будет заменить только первое вхождение «Яблока» на «банане»пример
Это простой скрипт: Примечание редактора: работает только с GNU
sed
.Первые два параметра
0
и/Apple/
являются спецификатором диапазона. Этоs/Apple/Banana/
то, что выполняется в этом диапазоне. Таким образом, в этом случае «в диапазоне от начала (0
) до первого экземпляраApple
, заменитьApple
наBanana
. Только первыйApple
будет заменен.Предыстория: в традиционном
sed
спецификаторе диапазона также «начинаются здесь» и «заканчиваются здесь» (включительно). Однако самое низкое «начало» - это первая строка (строка 1), и если «конец здесь» является регулярным выражением, то оно пытается найти совпадение только на следующей строке после «начала», поэтому самым ранним возможным концом является строка 2. Таким образом, поскольку диапазон включается, наименьший возможный диапазон равен «2 линиям», а наименьший начальный диапазон - это строки 1 и 2 (т. Е. Если в строке 1 есть вхождение, вхождения в строке 2 также будут изменены, что нежелательно в данном случае). ).GNU
sed добавляет свое собственное расширение, позволяющее указывать начало как «псевдо»,line 0
так что конец диапазона может бытьline 1
, позволяя ему диапазон «только первая строка»Или упрощенная версия (пустой RE-подобный
//
означает повторное использование указанного ранее, так что это эквивалентно):И фигурные скобки являются необязательными для
s
команды, так что это также эквивалентно:Все это работает
sed
только на GNU .Вы также можете установить GNU sed на OS X, используя homebrew
brew install gnu-sed
.источник
sed: 1: "…": bad flag in substitute command: '}'
sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'
. От ответа @ MikhailVS (в настоящее время) ниже.sed '0,/foo/s/foo/bar/'
sed: -e expression #1, char 3: unexpected
, `` с этимэто сработало для меня.
пример
Примечание редактора: оба работают только с GNU
sed
.источник
sed '1,/pattern/s/pattern/replacement/' filename
работает только в том случае, если «шаблон не будет отображаться в первой строке» на Mac. Я удалю свой предыдущий комментарий, так как он не точный. Подробности можно найти здесь ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). Ответ Энди работает только для GNU sed, но не для Mac.Обзор из многих полезных существующих ответов , дополненных с пояснениями :
В приведенных здесь примерах используется упрощенный вариант использования: замените слово «foo» на «bar» только в первой строке соответствия.
Благодаря использованию ANSI C-строк в кавычках (
$'...'
) , чтобы обеспечить выборки входных линий,bash
,ksh
илиzsh
предполагаются в качестве оболочки.sed
Только GNU :Anwswer Бен Hoffstein в показывает нам , что GNU обеспечивает расширение к спецификации POSIX для
sed
, что позволяет следующей формы 2-адрес :0,/re/
(re
представляет собой произвольное регулярное выражение здесь).0,/re/
позволяет регулярному выражению совпадать и в самой первой строке . Другими словами: такой адрес создаст диапазон от 1-й строки до и включая строку, которая соответствуетre
-re
происходит ли в 1-й строке или в любой последующей строке.1,/re/
, которая создает диапазон, который соответствует от 1-й строки до и включает строку, которая соответствуетre
в последующих строках; другими словами: это не обнаружит первоеre
совпадение, если оно произошло в 1-й строке, а также предотвратит использование сокращения//
для повторного использования последнего использованного регулярного выражения (см. следующий пункт). 1Если вы объединяете
0,/re/
адрес сs/.../.../
вызовом (подстановки), который использует то же регулярное выражение, ваша команда будет эффективно выполнять подстановку только в первой соответствующей строкеre
.sed
обеспечивает удобный ярлык для многократного использования самого последнего применяется регулярное выражение : пустой пары разделителей,//
.Только для POSIX-функций,
sed
таких как BSD (macOS)sed
(также будет работать с GNUsed
):Поскольку
0,/re/
невозможно использовать и форма1,/re/
не обнаружит,re
происходит ли это в самой первой строке (см. Выше), требуется специальная обработка для 1-й строки .В ответе MikhailVS упоминается методика, приведенная здесь на конкретном примере:
Примечание:
Пустой
//
ярлык регулярного выражения используется здесь дважды: один раз для конечной точки диапазона и один раз вs
вызове; в обоих случаях регулярное выражениеfoo
неявно используется повторно, что позволяет нам не дублировать его, что делает как более короткий, так и более понятный код.POSIX
sed
нужны реальные переводы строк после определенных функций, таких как после имени метки или даже ее пропуска, как в случае сt
здесь; Стратегическое разделение сценария на несколько-e
вариантов является альтернативой использованию фактических строк новой строки: заканчивайте каждый-e
фрагмент сценария там, где обычно должен идти символ новой строки.1 s/foo/bar/
заменяет толькоfoo
на 1-й строке, если найден там. Если это так, происходитt
переход к концу скрипта (пропускаются оставшиеся команды в строке). (t
Функция переходит к метке только в том случае, если самый последнийs
вызов выполнил фактическую замену; при отсутствии метки, как в данном случае, конец сценария разветвляется).Когда это происходит, адрес диапазона
1,//
, который обычно находит первое вхождение, начиная со строки 2 , не будет совпадать, и диапазон не будет обрабатываться, поскольку адрес оценивается, когда текущая строка уже существует2
.И наоборот, если на 1-й строке нет совпадений,
1,//
будет введено и найдет истинное первое совпадение.Чистый эффект такой же , как и с GNU
sed
«s0,/re/
: только первое вхождение заменяется, происходит ли это на 1 - й линии , или любой другой.Бездиапазонные подходы
Ответ Потонга демонстрирует петлевые техники, которые обходят необходимость в диапазоне ; так как он использует синтаксис GNU
sed
, вот POSIX-совместимые эквиваленты :Техника цикла 1: при первом совпадении выполните подстановку, затем введите цикл, который просто печатает оставшиеся строки как есть :
Техника цикла 2, только для небольших файлов : прочитать весь ввод в память, а затем выполнить одну подстановку .
1 1.61803 предоставляет примеры того, что происходит с
1,/re/
последующим и без негоs//
:-
sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
урожайность$'1bar\n2bar'
; то есть обе строки были обновлены, потому что номер строки1
соответствует 1-й строке, а регулярное выражение/foo/
- конец диапазона - затем ищется только для начала на следующей строке. Следовательно, в этом случае выбираются обе строки, иs/foo/bar/
замена выполняется для обеих из них.-
sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
терпит неудачу : сsed: first RE may not be empty
(BSD / macOS) иsed: -e expression #1, char 0: no previous regular expression
(GNU), потому что во время обработки 1-й строки (из-за номера строки,1
начинающего диапазон), регулярное выражение еще не применялось, поэтому//
не относится ни к чему.За исключением
sed
специального0,/re/
синтаксиса GNU , любой диапазон, начинающийся с номера строки, эффективно исключает использование//
.источник
Вы можете использовать awk, чтобы сделать что-то подобное ..
Объяснение:
Запускает оператор действия между {}, когда строка соответствует «#include», и мы еще не обработали его.
Это печатает #include "newfile.h", нам нужно экранировать кавычки. Затем мы устанавливаем переменную done на 1, чтобы не добавлять больше включений.
Это означает «распечатать строку» - пустое действие по умолчанию печатает $ 0, что выводит всю строку. Один вкладыш и его легче понять, чем sed IMO :-)
источник
awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Довольно полный сборник ответов на linuxtopia sed FAQ . Это также подчеркивает, что некоторые ответы, которые предоставили люди, не будут работать с не-GNU версией sed, например
в не-GNU версии должно быть
Однако эта версия не будет работать с GNU SED.
Вот версия, которая работает с обоими:
например:
источник
Как работает этот сценарий: для строк между 1 и первой
#include
(после строки 1), если строка начинается с#include
, тогда добавьте указанную строку.Однако, если первый
#include
находится в строке 1, то и строка 1, и следующая последующая#include
будут иметь добавленную строку. Если вы используете GNUsed
, у него есть расширение, в котором0,/^#include/
(вместо1,
) все будет делать правильно.источник
Просто добавьте номер вхождения в конце:
источник
sed
указывает команду замены с помощью:[2addr]s/BRE/replacement/flags
и отмечает, что «значение флагов должно быть нулем или более из: n заменять только n-е вхождение только BRE, найденного в пространстве шаблона». Таким образом, по крайней мере в POSIX 2008, трейлинг1
не являетсяsed
расширением GNU . В самом деле, даже в стандарте SUS / POSIX 1997 это поддерживалось, поэтому я был совершенно не в себе в 2008 году.Возможное решение:
Объяснение:
источник
sed: file me4.sed line 4: ":" lacks a label
Я знаю, что это старый пост, но у меня было решение, которое я использовал:
В основном используйте grep, чтобы напечатать первое вхождение и остановиться там. Кроме того , печать номер линии ИЭ
5:line
. Передайте это в sed и удалите: и что-нибудь после, чтобы у вас остался номер строки. Передайте это в sed, который добавляет s /.*/ вместо конечного числа, что приводит к 1-строчному сценарию, который передается в последний sed для запуска в качестве сценария для файла.так что если regex =
#include
и replace =blah
и первый вхождение grep находит в строке 5, то данные, переданные последнему sed, будут5s/.*/blah/
.Работает, даже если первое вхождение находится на первой строке.
источник
sed -f -
а некоторые нет, но вы можете обойти это :)Если кто-то пришел сюда, чтобы заменить символ для первого вхождения во всех строках (как я), используйте это:
Например, изменив 1 на 2, вы можете заменить только все вторые a.
источник
's/a/b/'
значитmatch a
, иdo just first match
for every matching line
С
-z
опцией GNU sed вы можете обработать весь файл, как если бы он был только одной строкой. Таким образом, as/…/…/
заменит только первое совпадение во всем файле. Помните:s/…/…/
только заменяет первое совпадение в каждой строке, но с-z
опциейsed
обрабатывает весь файл как одну строку.В общем случае вам нужно переписать выражение sed, поскольку пространство шаблонов теперь содержит весь файл, а не одну строку. Некоторые примеры:
s/text.*//
можно переписать какs/text[^\n]*//
.[^\n]
соответствует всему, кроме символа новой строки.[^\n]*
будет соответствовать всем символам послеtext
до новой строки.s/^text//
можно переписать какs/(^|\n)text//
.s/text$//
можно переписать какs/text(\n|$)//
.источник
я бы сделал это с помощью сценария awk:
затем запустите его с помощью awk:
может быть небрежно, я новичок в этом.
источник
В качестве альтернативного предложения вы можете посмотреть на
ed
команду.источник
Наконец-то я понял, что это работает в скрипте Bash, который используется для вставки уникальной метки времени в каждый элемент RSS-канала:
Изменяет только первое вхождение.
${nowms}
время в миллисекундах, установленное скриптом Perl;$counter
счетчик, используемый для управления циклом в скрипте;\
позволяет продолжить выполнение команды на следующей строке.Файл считывается, и стандартный вывод перенаправляется в рабочий файл.
Насколько я понимаю,
1,/====RSSpermalink====/
Sed сообщает, когда нужно остановиться, устанавливая ограничение диапазона, а затемs/====RSSpermalink====/${nowms}/
знакомая команда SED для замены первой строки второй.В моем случае я поставил команду в двойных кавычках, потому что я использую ее в скрипте Bash с переменными.
источник
Используйте FreeBSD
ed
и избегайтеed
ошибки «нет соответствия», еслиinclude
в файле нет оператора для обработки:источник
Это может работать для вас (GNU sed):
или если память не проблема:
источник
Следующая команда удаляет первое вхождение строки в файле. Это также удаляет пустую строку. Он представлен в файле XML, но он будет работать с любым файлом.
Полезно, если вы работаете с XML-файлами и хотите удалить тег. В этом примере он удаляет первое вхождение тега «isTag».
Команда:
Исходный файл (source.txt)
Файл результатов (output.txt)
PS: он не работал для меня на Solaris SunOS 5.10 (довольно старый), но он работает на Linux 2.6, sed версия 4.1.5
источник
sed
(следовательно, оно не работает с Solaris). Вы должны удалить это, пожалуйста - это действительно не дает отличительной новой информации для вопроса, которому было уже 4 с половиной года, когда вы ответили. Конечно, у него есть работающий пример, но он имеет дискуссионную ценность, когда у вопроса столько же ответов, сколько у этого.Ничего нового, но, возможно, немного более конкретный ответ:
sed -rn '0,/foo(bar).*/ s%%\1%p'
Пример:
xwininfo -name unity-launcher
производит вывод как:Извлечение идентификатора окна с помощью
xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'
:источник
POSIXly (также действует в sed), используется только одно регулярное выражение, требуется память только для одной строки (как обычно):
Разъяснение:
источник
Возможный вариант использования может заключаться в том, что ваши случаи распространяются по всему файлу, но вы знаете, что ваша единственная проблема - в первых 10, 20 или 100 строках.
Тогда простая адресация этих строк устраняет проблему - даже если формулировка ОП касается только первой.
источник
Возможным решением здесь может быть указание компилятору включить заголовок, не упоминая его в исходных файлах. В GCC есть следующие опции:
Компилятор Microsoft имеет параметр / FI (принудительное включение).
Эта функция может быть полезна для некоторых общих заголовков, таких как конфигурация платформы. Makefile ядра Linux использует
-include
для этого.источник
источник