Как использовать sed, чтобы заменить только первое вхождение в файле?

217

Я хотел бы обновить большое количество исходных файлов C ++ с помощью дополнительной директивы include перед любым существующим #include. Для такого рода задач я обычно использую небольшой скрипт bash с sed, чтобы переписать файл.

Как мне sedзаменить только первое вхождение строки в файле, а не заменять каждое вхождение?

Если я использую

sed s/#include/#include "newfile.h"\n#include/

он заменяет все #include.

Альтернативные предложения для достижения того же самого также приветствуются.

Дэвид Диббен
источник

Ответы:

137
 # sed script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

или, если вы предпочитаете: Примечание редактора: работает только с GNU sed .

sed '0,/foo/s//bar/' file 

Источник

Бен Хоффштейн
источник
86
Я думаю, что я предпочитаю решение «или, если вы предпочитаете». Было бы также хорошо объяснить ответы - и дать ответ непосредственно на вопрос, а затем обобщить, а не только обобщать. Но хороший ответ.
Джонатан Леффлер
7
Для пользователей Mac, вы должны заменить 0 на 1, поэтому: sed '1, / RE / s // to_that /' file
mhost
1
@mhost Нет, он будет заменен, если шаблон найден в строке # 1, а не если шаблон находится на другой строке, но все еще является первым найденным шаблоном. PS следует отметить, что это 0,работает только сgnu sed
Jotne
11
Может кто-нибудь объяснить, пожалуйста, «или, если вы предпочитаете» решение? Я не знаю, где поставить шаблон "из".
Жан-Люк Насиф Коэльо
3
@ Jean-LucNacifCoelho: использование s//- то есть пустое регулярное выражение - означает, что последнее примененное регулярное выражение неявно используется повторно; в этом случае RE. Этот удобный ярлык означает, что вам не нужно дублировать регулярное выражение в конце sвызова.
mklement0
290

sedСкрипт , который будет заменить только первое вхождение «Яблока» на «банане»

пример

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

Это простой скрипт: Примечание редактора: работает только с GNU sed .

sed '0,/Apple/{s/Apple/Banana/}' input_filename

Первые два параметра 0и /Apple/являются спецификатором диапазона. Это s/Apple/Banana/то, что выполняется в этом диапазоне. Таким образом, в этом случае «в диапазоне от начала ( 0) до первого экземпляра Apple, заменить Appleна Banana. Только первый Appleбудет заменен.

Предыстория: в традиционном sedспецификаторе диапазона также «начинаются здесь» и «заканчиваются здесь» (включительно). Однако самое низкое «начало» - это первая строка (строка 1), и если «конец здесь» является регулярным выражением, то оно пытается найти совпадение только на следующей строке после «начала», поэтому самым ранним возможным концом является строка 2. Таким образом, поскольку диапазон включается, наименьший возможный диапазон равен «2 линиям», а наименьший начальный диапазон - это строки 1 и 2 (т. Е. Если в строке 1 есть вхождение, вхождения в строке 2 также будут изменены, что нежелательно в данном случае). ). GNUsed добавляет свое собственное расширение, позволяющее указывать начало как «псевдо», line 0так что конец диапазона может быть line 1, позволяя ему диапазон «только первая строка»

Или упрощенная версия (пустой RE-подобный //означает повторное использование указанного ранее, так что это эквивалентно):

sed '0,/Apple/{s//Banana/}' input_filename

И фигурные скобки являются необязательными для sкоманды, так что это также эквивалентно:

sed '0,/Apple/s//Banana/' input_filename

Все это работает sedтолько на GNU .

Вы также можете установить GNU sed на OS X, используя homebrew brew install gnu-sed.

Тим
источник
167
переведено на человеческий язык: начните со строки 0, продолжайте, пока не найдете «Apple», выполните подстановку в фигурных скобках. cfr: grymoire.com/Unix/Sed.html#uh-29
мариотомо
8
На OS X я получаюsed: 1: "…": bad flag in substitute command: '}'
ELLIOTTCABLE
5
@ELLIOTTCABLE на OS X, используйте sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. От ответа @ MikhailVS (в настоящее время) ниже.
DJB
8
Работает без скобок тоже:sed '0,/foo/s/foo/bar/'
Innokenty
5
Я получаю sed: -e expression #1, char 3: unexpected , `` с этим
Джонатан
56
sed '0,/pattern/s/pattern/replacement/' filename

это сработало для меня.

пример

sed '0,/<Menu>/s/<Menu>/<Menu><Menu>Sub menu<\/Menu>/' try.txt > abc.txt

Примечание редактора: оба работают только с GNU sed .

Sushil
источник
2
@Landys это все еще заменяет экземпляры в других строках; не только первый случай
сарат
1
@ Сарат Да, ты прав. sed '1,/pattern/s/pattern/replacement/' filenameработает только в том случае, если «шаблон не будет отображаться в первой строке» на Mac. Я удалю свой предыдущий комментарий, так как он не точный. Подробности можно найти здесь ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). Ответ Энди работает только для GNU sed, но не для Mac.
Landys
45

Обзор из многих полезных существующих ответов , дополненных с пояснениями :

В приведенных здесь примерах используется упрощенный вариант использования: замените слово «foo» на «bar» только в первой строке соответствия.
Благодаря использованию ANSI C-строк в кавычках ( $'...') , чтобы обеспечить выборки входных линий, bash, kshили zshпредполагаются в качестве оболочки.


sedТолько GNU :

Anwswer Бен Hoffstein в показывает нам , что GNU обеспечивает расширение к спецификации POSIX дляsed , что позволяет следующей формы 2-адрес : 0,/re/( reпредставляет собой произвольное регулярное выражение здесь).

0,/re/позволяет регулярному выражению совпадать и в самой первой строке . Другими словами: такой адрес создаст диапазон от 1-й строки до и включая строку, которая соответствует re- reпроисходит ли в 1-й строке или в любой последующей строке.

  • Сравните это с POSIX-совместимой формой 1,/re/, которая создает диапазон, который соответствует от 1-й строки до и включает строку, которая соответствует reв последующих строках; другими словами: это не обнаружит первое reсовпадение, если оно произошло в 1-й строке, а также предотвратит использование сокращения// для повторного использования последнего использованного регулярного выражения (см. следующий пункт). 1

Если вы объединяете 0,/re/адрес с s/.../.../вызовом (подстановки), который использует то же регулярное выражение, ваша команда будет эффективно выполнять подстановку только в первой соответствующей строке re.
sedобеспечивает удобный ярлык для многократного использования самого последнего применяется регулярное выражение : пустой пары разделителей,// .

$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo' 
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

Только для POSIX-функций, sedтаких как BSD (macOS)sed (также будет работать с GNU sed ):

Поскольку 0,/re/невозможно использовать и форма 1,/re/не обнаружит, reпроисходит ли это в самой первой строке (см. Выше), требуется специальная обработка для 1-й строки .

В ответе MikhailVS упоминается методика, приведенная здесь на конкретном примере:

$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar         # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo

Примечание:

  • Пустой //ярлык регулярного выражения используется здесь дважды: один раз для конечной точки диапазона и один раз в sвызове; в обоих случаях регулярное выражение fooнеявно используется повторно, что позволяет нам не дублировать его, что делает как более короткий, так и более понятный код.

  • POSIX sedнужны реальные переводы строк после определенных функций, таких как после имени метки или даже ее пропуска, как в случае с tздесь; Стратегическое разделение сценария на несколько -eвариантов является альтернативой использованию фактических строк новой строки: заканчивайте каждый -eфрагмент сценария там, где обычно должен идти символ новой строки.

1 s/foo/bar/заменяет только fooна 1-й строке, если найден там. Если это так, происходит tпереход к концу скрипта (пропускаются оставшиеся команды в строке). ( tФункция переходит к метке только в том случае, если самый последний sвызов выполнил фактическую замену; при отсутствии метки, как в данном случае, конец сценария разветвляется).

Когда это происходит, адрес диапазона 1,//, который обычно находит первое вхождение, начиная со строки 2 , не будет совпадать, и диапазон не будет обрабатываться, поскольку адрес оценивается, когда текущая строка уже существует 2.

И наоборот, если на 1-й строке нет совпадений, 1,// будет введено и найдет истинное первое совпадение.

Чистый эффект такой же , как и с GNU sed«s 0,/re/: только первое вхождение заменяется, происходит ли это на 1 - й линии , или любой другой.


Бездиапазонные подходы

Ответ Потонга демонстрирует петлевые техники, которые обходят необходимость в диапазоне ; так как он использует синтаксис GNU sed , вот POSIX-совместимые эквиваленты :

Техника цикла 1: при первом совпадении выполните подстановку, затем введите цикл, который просто печатает оставшиеся строки как есть :

$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

Техника цикла 2, только для небольших файлов : прочитать весь ввод в память, а затем выполнить одну подстановку .

$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo

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 , любой диапазон, начинающийся с номера строки, эффективно исключает использование //.

mklement0
источник
25

Вы можете использовать awk, чтобы сделать что-то подобное ..

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

Объяснение:

/#include/ && !done

Запускает оператор действия между {}, когда строка соответствует «#include», и мы еще не обработали его.

{print "#include \"newfile.h\""; done=1;}

Это печатает #include "newfile.h", нам нужно экранировать кавычки. Затем мы устанавливаем переменную done на 1, чтобы не добавлять больше включений.

1;

Это означает «распечатать строку» - пустое действие по умолчанию печатает $ 0, что выводит всю строку. Один вкладыш и его легче понять, чем sed IMO :-)

richq
источник
4
Этот ответ более переносим, ​​чем решения sed, которые полагаются на gnu sed и т. Д. (Например, sed в OS-X - отстой!)
Джей Тейлор,
1
Это действительно более понятно, но для меня это добавляет строку вместо того, чтобы заменить ее; Используемая команда: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
Убийца зерновых
Здесь та же самая понятная команда, но она добавляет строку над найденной строкой вместо ее замены
Flion
1
Ответ вполне читабелен. Вот моя версия, которая заменяет строку вместо добавления новой строки. awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Орсирис де Йонг
18

Довольно полный сборник ответов на linuxtopia sed FAQ . Это также подчеркивает, что некоторые ответы, которые предоставили люди, не будут работать с не-GNU версией sed, например

sed '0,/RE/s//to_that/' file

в не-GNU версии должно быть

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

Однако эта версия не будет работать с GNU SED.

Вот версия, которая работает с обоими:

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

например:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
MikhailVS
источник
Действительно, тестируется работа на Ubuntu Linux v16 и FreeBSD v10.2. Спасибо вам.
Сопалахо де Арриерес
12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

Как работает этот сценарий: для строк между 1 и первой #include(после строки 1), если строка начинается с #include, тогда добавьте указанную строку.

Однако, если первый #includeнаходится в строке 1, то и строка 1, и следующая последующая #includeбудут иметь добавленную строку. Если вы используете GNU sed, у него есть расширение, в котором 0,/^#include/(вместо 1,) все будет делать правильно.

Крис Шут-Янг
источник
11

Просто добавьте номер вхождения в конце:

sed s/#include/#include "newfile.h"\n#include/1
Юнексист
источник
7
К сожалению, это не работает. Он заменяет только первое вхождение в каждой строке файла, а не первое вхождение в файле.
Дэвид Диббен
1
Кроме того, это расширение GNU sed, а не стандартная функция sed.
Джонатан Леффлер
10
Хммм ... время проходит. POSIX 2008/2013 for sedуказывает команду замены с помощью: [2addr]s/BRE/replacement/flagsи отмечает, что «значение флагов должно быть нулем или более из: n заменять только n-е вхождение только BRE, найденного в пространстве шаблона». Таким образом, по крайней мере в POSIX 2008, трейлинг 1не является sedрасширением GNU . В самом деле, даже в стандарте SUS / POSIX 1997 это поддерживалось, поэтому я был совершенно не в себе в 2008 году.
Джонатан Леффлер
7

Возможное решение:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :a
    n
    ba

Объяснение:

  • читать строки, пока мы не найдем #include, распечатать эти строки, а затем начать новый цикл
  • вставить новую строку включения
  • введите цикл, который просто читает строки (по умолчанию sed также будет печатать эти строки), отсюда мы не вернемся к первой части скрипта
mitchnull
источник
sed: file me4.sed line 4: ":" lacks a label
rogerdpack
очевидно, что-то изменилось в последних версиях sed, и пустые метки больше не допускаются. обновил ответ
mitchnull
4

Я знаю, что это старый пост, но у меня было решение, которое я использовал:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

В основном используйте grep, чтобы напечатать первое вхождение и остановиться там. Кроме того , печать номер линии ИЭ 5:line. Передайте это в sed и удалите: и что-нибудь после, чтобы у вас остался номер строки. Передайте это в sed, который добавляет s /.*/ вместо конечного числа, что приводит к 1-строчному сценарию, который передается в последний sed для запуска в качестве сценария для файла.

так что если regex = #includeи replace = blahи первый вхождение grep находит в строке 5, то данные, переданные последнему sed, будут 5s/.*/blah/.

Работает, даже если первое вхождение находится на первой строке.

Майкл Эдвардс
источник
Я совершенно ненавижу многострочные сценарии sed или команды sed с помощью чего-либо, кроме s и белья, поэтому я согласен с этим подходом. Вот что я использовал для своего сценария использования (работает с bash): filepath = / etc / hosts; patt = '^ \ (127 \ .0 \ .0 \ .1. * \)'; repl = '\ 1 newhostalias'; sed $ (IFS =: linearray = ($ (grep -E -m 1 -n "$ patt" "$ filepath")) && echo $ {linearray [0]}) s / "$ patt" / "$ repl" / "$ filepath"
четность3
Оно работает. Хотя только с sed, которые достаточно умны, чтобы принять, sed -f -а некоторые нет, но вы можете обойти это :)
rogerdpack
3

Если кто-то пришел сюда, чтобы заменить символ для первого вхождения во всех строках (как я), используйте это:

sed '/old/s/old/new/1' file

-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12

Например, изменив 1 на 2, вы можете заменить только все вторые a.

FatihSarigol
источник
2
Вам не нужно делать все это, 's/a/b/'значит match a, иdo just first match for every matching line
Самвин
Я с Самвин. Также это не отвечает на вопрос, заданный здесь . Я бы посоветовал удалить этот ответ.
Socowi
Вопрос был «первое вхождение в файл», а не «первое вхождение в строку»
Мануэль Ромейро
3

С -zопцией GNU sed вы можете обработать весь файл, как если бы он был только одной строкой. Таким образом, a s/…/…/заменит только первое совпадение во всем файле. Помните: s/…/…/только заменяет первое совпадение в каждой строке, но с -zопцией sedобрабатывает весь файл как одну строку.

sed -z 's/#include/#include "newfile.h"\n#include'

В общем случае вам нужно переписать выражение sed, поскольку пространство шаблонов теперь содержит весь файл, а не одну строку. Некоторые примеры:

  • s/text.*//можно переписать как s/text[^\n]*//. [^\n]соответствует всему, кроме символа новой строки. [^\n]*будет соответствовать всем символам после textдо новой строки.
  • s/^text//можно переписать как s/(^|\n)text//.
  • s/text$//можно переписать как s/text(\n|$)//.
Socowi
источник
2

я бы сделал это с помощью сценария awk:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}    
END {}

затем запустите его с помощью awk:

awk -f awkscript headerfile.h > headerfilenew.h

может быть небрежно, я новичок в этом.

wakingrufus
источник
2

В качестве альтернативного предложения вы можете посмотреть на edкоманду.

man 1 ed

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   /# *include/i
   #include "newfile.h"
   .
   ,p
   q
EOF
Timo
источник
2

Наконец-то я понял, что это работает в скрипте Bash, который используется для вставки уникальной метки времени в каждый элемент RSS-канала:

        sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
            production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter

Изменяет только первое вхождение.

${nowms}время в миллисекундах, установленное скриптом Perl; $counterсчетчик, используемый для управления циклом в скрипте; \позволяет продолжить выполнение команды на следующей строке.

Файл считывается, и стандартный вывод перенаправляется в рабочий файл.

Насколько я понимаю, 1,/====RSSpermalink====/Sed сообщает, когда нужно остановиться, устанавливая ограничение диапазона, а затем s/====RSSpermalink====/${nowms}/знакомая команда SED для замены первой строки второй.

В моем случае я поставил команду в двойных кавычках, потому что я использую ее в скрипте Bash с переменными.

Майкл Кук
источник
2

Используйте FreeBSD ed и избегайте edошибки «нет соответствия», если includeв файле нет оператора для обработки:

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917 
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   ,g/# *include/u\
   u\
   i\
   #include "newfile.h"\
   .
   ,p
   q
EOF
nazq
источник
Это удивительно похоже на ответ Тимо , но было добавлено более года спустя.
Джонатан Леффлер
2

Это может работать для вас (GNU sed):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

или если память не проблема:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...
Potong
источник
0

Следующая команда удаляет первое вхождение строки в файле. Это также удаляет пустую строку. Он представлен в файле XML, но он будет работать с любым файлом.

Полезно, если вы работаете с XML-файлами и хотите удалить тег. В этом примере он удаляет первое вхождение тега «isTag».

Команда:

sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//}  -e 's/ *$//' -e  '/^$/d'  source.txt > output.txt

Исходный файл (source.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <isTag>false</isTag>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

Файл результатов (output.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

PS: он не работал для меня на Solaris SunOS 5.10 (довольно старый), но он работает на Linux 2.6, sed версия 4.1.5

Андреас Панайотидис
источник
Это удивительно похоже на ту же основную идею, что и ряд предыдущих ответов, с тем же предупреждением, что оно работает только с GNU sed(следовательно, оно не работает с Solaris). Вы должны удалить это, пожалуйста - это действительно не дает отличительной новой информации для вопроса, которому было уже 4 с половиной года, когда вы ответили. Конечно, у него есть работающий пример, но он имеет дискуссионную ценность, когда у вопроса столько же ответов, сколько у этого.
Джонатан Леффлер
0

Ничего нового, но, возможно, немного более конкретный ответ: sed -rn '0,/foo(bar).*/ s%%\1%p'

Пример: xwininfo -name unity-launcherпроизводит вывод как:

xwininfo: Window id: 0x2200003 "unity-launcher"

  Absolute upper-left X:  -2980
  Absolute upper-left Y:  -198
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 2880
  Height: 98
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 0
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: ForgetGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +-2980+-198  -2980+-198  -2980-1900  +-2980-1900
  -geometry 2880x98+-2980+-198

Извлечение идентификатора окна с помощью xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p':

0x2200003
Стивен Недзельски
источник
0

POSIXly (также действует в sed), используется только одно регулярное выражение, требуется память только для одной строки (как обычно):

sed '/\(#include\).*/!b;//{h;s//\1 "newfile.h"/;G};:1;n;b1'

Разъяснение:

sed '
/\(#include\).*/!b          # Only one regex used. On lines not matching
                            # the text  `#include` **yet**,
                            # branch to end, cause the default print. Re-start.
//{                         # On first line matching previous regex.
    h                       # hold the line.
    s//\1 "newfile.h"/      # append ` "newfile.h"` to the `#include` matched.
    G                       # append a newline.
  }                         # end of replacement.
:1                          # Once **one** replacement got done (the first match)
n                           # Loop continually reading a line each time
b1                          # and printing it by default.
'                           # end of sed script.
Исаак
источник
0

Возможный вариант использования может заключаться в том, что ваши случаи распространяются по всему файлу, но вы знаете, что ваша единственная проблема - в первых 10, 20 или 100 строках.

Тогда простая адресация этих строк устраняет проблему - даже если формулировка ОП касается только первой.

sed '1,10s/#include/#include "newfile.h"\n#include/'
sastorsl
источник
0

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

   -include file
       Process file as if "#include "file"" appeared as the first line of
       the primary source file.  However, the first directory searched for
       file is the preprocessor's working directory instead of the
       directory containing the main source file.  If not found there, it
       is searched for in the remainder of the "#include "..."" search
       chain as normal.

       If multiple -include options are given, the files are included in
       the order they appear on the command line.

   -imacros file
       Exactly like -include, except that any output produced by scanning
       file is thrown away.  Macros it defines remain defined.  This
       allows you to acquire all the macros from a header without also
       processing its declarations.

       All files specified by -imacros are processed before all files
       specified by -include.

Компилятор Microsoft имеет параметр / FI (принудительное включение).

Эта функция может быть полезна для некоторых общих заголовков, таких как конфигурация платформы. Makefile ядра Linux использует -includeдля этого.

Kaz
источник