¿Cómo usar sed para reemplazar solo la primera aparición en un archivo?

217

Me gustaría actualizar una gran cantidad de archivos fuente C ++ con una directiva de inclusión adicional antes de cualquier #include existente. Para este tipo de tarea, normalmente uso un pequeño script bash con sed para volver a escribir el archivo.

¿Cómo puedo sedreemplazar solo la primera aparición de una cadena en un archivo en lugar de reemplazar cada aparición?

Si yo uso

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

reemplaza todos los #incluye.

Sugerencias alternativas para lograr lo mismo también son bienvenidas.

David Dibben
fuente

Respuestas:

135
 # 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---

o, si lo prefiere: Nota del editor: funciona solo con GNU sed .

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

Fuente

Ben Hoffstein
fuente
86
Creo que prefiero la solución 'o si prefieres'. También sería bueno explicar las respuestas, y hacer que la respuesta aborde la pregunta directamente, y luego generalice, en lugar de generalizar solamente. Pero buena respuesta.
Jonathan Leffler
77
Para su información para usuarios de Mac, debe reemplazar el 0 con un 1, por lo tanto: sed '1, / RE / s // to_that /' file
mhost
1
@mhost No, se reemplazará si el patrón se encuentra en la línea 1 y no si el patrón está en otra línea, pero sigue siendo el primer patrón encontrado. PD: Cabe mencionar que esto 0,solo funciona congnu sed
Jotne
11
¿Podría alguien explicarme la solución "o si prefiere"? No sé dónde poner el patrón "de".
Jean-Luc Nacif Coelho
3
@ Jean-LucNacifCoelho: el uso s//, es decir, una expresión regular vacía , significa que la expresión regular aplicada más recientemente se reutiliza implícitamente; en este caso, RE. Este conveniente acceso directo significa que no tiene que duplicar la expresión regular de fin de rango en su sllamada.
mklement0
289

Un sedscript que solo reemplazará la primera aparición de "Apple" por "Banana"

Ejemplo

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

Este es el guión simple: Nota del editor: funciona solo con GNU sed .

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

Los dos primeros parámetros 0y /Apple/son el especificador de rango. El s/Apple/Banana/es lo que se ejecuta dentro de ese rango. Entonces, en este caso "dentro del rango del principio ( 0) hasta la primera instancia de Apple, reemplace Applecon Banana. Solo Applese reemplazará la primera .

Antecedentes: en el tradicional, sedel especificador de rango también es "comenzar aquí" y "terminar aquí" (inclusive). Sin embargo, el "comienzo" más bajo es la primera línea (línea 1), y si el "final aquí" es una expresión regular, entonces solo se intenta hacer coincidir en la siguiente línea después de "comenzar", por lo que el final más temprano posible es la línea 2. Entonces, dado que el rango es inclusivo, el rango más pequeño posible es "2 líneas" y el rango inicial más pequeño es ambas líneas 1 y 2 (es decir, si hay una ocurrencia en la línea 1, las ocurrencias en la línea 2 también se cambiarán, no deseadas en este caso ) GNUsed agrega su propia extensión que permite especificar el inicio como "pseudo" line 0para que el final del rango pueda ser line 1, permitiéndole un rango de "solo la primera línea"

O una versión simplificada (un RE vacío como //significa reutilizar el especificado antes, por lo que esto es equivalente):

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

Y las llaves son opcionales para el scomando, por lo que esto también es equivalente:

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

Todo esto funciona sedsolo en GNU .

También puede instalar GNU sed en OS X usando homebrew brew install gnu-sed.

tim
fuente
166
traducido al lenguaje humano: comience en la línea 0, continúe hasta que coincida con 'Apple', ejecute la sustitución entre llaves. cfr: grymoire.com/Unix/Sed.html#uh-29
mariotomo
8
En OS X, lo entiendosed: 1: "…": bad flag in substitute command: '}'
ELLIOTTCABLE
55
@ELLIOTTCABLE en OS X, uso sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. De la respuesta de @ MikhailVS (actualmente) muy abajo.
djb
8
Funciona sin los paréntesis también:sed '0,/foo/s/foo/bar/'
Innokenty
55
Me sale sed: -e expression #1, char 3: unexpected '' con esto
Jonathan
56
sed '0,/pattern/s/pattern/replacement/' filename

Esto funcionó para mí.

ejemplo

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

Nota del editor: ambos funcionan solo con GNU sed .

Sushil
fuente
2
@Landys esto todavía reemplaza instancias en las otras líneas también; no solo la primera instancia
sarat
1
@sarat Sí, tienes razón. sed '1,/pattern/s/pattern/replacement/' filenamesolo funciona si "el patrón no se producirá en la primera línea" en Mac. Eliminaré mi comentario anterior ya que no es exacto. Los detalles se pueden encontrar aquí ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). La respuesta de Andy solo funciona para GNU sed, pero no para Mac.
Landys
45

Una descripción general de las muchas respuestas útiles existentes , complementadas con explicaciones :

Los ejemplos aquí usan un caso de uso simplificado: reemplace la palabra 'foo' con 'bar' en la primera línea coincidente solamente.
Debido al uso de cadenas entre comillas-C ANSI ( $'...') para proporcionar las líneas de entrada de muestra, bash, ksh, o zshse asume como la cáscara.


GNU sed solamente:

La respuesta de Ben Hoffstein nos muestra que GNU proporciona una extensión a la especificación POSIXsed que permite la siguiente forma de 2 direcciones : 0,/re/( rerepresenta una expresión regular arbitraria aquí).

0,/re/permite que la expresión regular coincida en la primera línea también . En otras palabras: dicha dirección creará un rango desde la primera línea hasta la línea que coincida re, ya sea que reocurra en la primera línea o en cualquier línea posterior.

  • Compare esto con el formulario compatible con POSIX 1,/re/, que crea un rango que coincide desde la primera línea hasta e incluye la línea que coincide reen las líneas posteriores ; en otras palabras: esto no detectará la primera aparición de una recoincidencia si ocurre en la primera línea y también evita el uso de la taquigrafía// para la reutilización de la expresión regular utilizada más recientemente (ver el siguiente punto). 1

Si combina una 0,/re/dirección con una s/.../.../llamada (de sustitución) que usa la misma expresión regular, su comando efectivamente solo realizará la sustitución en la primera línea que coincida re.
sedproporciona un cómodo acceso directo para la reutilización de la expresión regular más recientemente aplicado : un vacío par delimitador,// .

$ 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

Un POSIX-solo sedcaracterísticas como BSD (macOS)sed (también funcionará con GNU sed ):

Como 0,/re/no se puede usar y el formulario 1,/re/no detectará resi ocurre en la primera línea (ver arriba), se requiere un manejo especial para la primera línea .

La respuesta de MikhailVS menciona la técnica, puesta en un ejemplo concreto aquí:

$ 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

Nota:

  • El //atajo de expresiones regulares vacías se emplea dos veces aquí: una para el punto final del rango y otra en la sllamada; en ambos casos, regex foose reutiliza implícitamente, lo que nos permite no tener que duplicarlo, lo que hace que el código sea más corto y más fácil de mantener.

  • POSIX sednecesita nuevas líneas reales después de ciertas funciones, como después del nombre de una etiqueta o incluso su omisión, como es el caso taquí; dividir estratégicamente el guión en múltiples -eopciones es una alternativa al uso de una nueva línea real: finalice cada -efragmento del guión donde normalmente debería ir una nueva línea.

1 s/foo/bar/reemplaza solo fooen la primera línea, si se encuentra allí. Si es así, se tbifurca al final del script (omite los comandos restantes en la línea). (La tfunción se ramifica a una etiqueta solo si la sllamada más reciente realizó una sustitución real; en ausencia de una etiqueta, como es el caso aquí, se ramifica el final del script).

Cuando eso sucede, la dirección de rango 1,//, que normalmente encuentra la primera aparición a partir de la línea 2 , no coincidirá, y el rango no se procesará, porque la dirección se evalúa cuando la línea actual ya está 2.

Por el contrario, si no hay una coincidencia en la primera línea, 1,// se ingresará y encontrará la primera coincidencia verdadera.

El efecto neto es el mismo que con GNU sed 's 0,/re/: sólo la primera aparición se sustituye, si se produce en la primera línea de o cualquier otro.


Enfoques sin rango

la respuesta de potong demuestra técnicas de bucle que evitan la necesidad de un rango ; ya que usa GNU sed sintaxis , aquí están los equivalentes compatibles con POSIX :

Técnica de bucle 1: en la primera coincidencia, realice la sustitución, luego ingrese un bucle que simplemente imprima las líneas restantes tal como están :

$ 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

Técnica de bucle 2, solo para archivos pequeños : lea toda la entrada en la memoria y luego realice una única sustitución .

$ 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 proporciona ejemplos de lo que sucede con 1,/re/, con y sin posterioridad s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendimientos $'1bar\n2bar'; es decir, ambas líneas se actualizaron, porque el número de línea 1coincide con la primera línea y la expresión regular /foo/, el final del rango, solo se busca para comenzar en la siguiente línea. Por lo tanto, ambas líneas se seleccionan en este caso, y la s/foo/bar/sustitución se realiza en ambas.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' falla : con sed: first RE may not be empty(BSD / macOS) y sed: -e expression #1, char 0: no previous regular expression(GNU), porque, en el momento en que se procesa la primera línea (debido al número de línea que 1comienza el rango), todavía no se ha aplicado expresión regular, por lo que//no se refiere a nada
Con la excepción de sedla 0,/re/sintaxis especial de GNU , cualquier rango que comience con un número de línea efectivamente impide el uso de //.

mklement0
fuente
25

Podrías usar awk para hacer algo similar ...

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

Explicación:

/#include/ && !done

Ejecuta la declaración de acción entre {} cuando la línea coincide con "#include" y aún no la hemos procesado.

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

Esto imprime #include "newfile.h", necesitamos escapar de las comillas. Luego establecemos la variable done en 1, por lo que no agregamos más inclusiones.

1;

Esto significa "imprimir la línea" - una acción vacía por defecto imprime $ 0, que imprime toda la línea. Un trazador de líneas y más fácil de entender que sed IMO :-)

richq
fuente
44
Esta respuesta es más portátil que las soluciones de sed, que dependen de la sed de GNU etc .. (por ejemplo, sed en OS-X chupa!)
Jay Taylor
1
Esto es realmente más comprensible, pero para mí agrega una línea en lugar de reemplazarla; comando utilizado: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
Cereal Killer
lo mismo aquí, el comando más comprensible, pero agrega una línea sobre la cadena encontrada en lugar de reemplazarla
Flion
1
La respuesta es bastante legible. Aquí está mi versión que reemplaza la cadena en lugar de agregar una nueva línea. awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Orsiris de Jong
18

Una colección bastante completa de respuestas sobre linuxtopia sed FAQ . También destaca que algunas respuestas que las personas proporcionaron no funcionarán con una versión de sed que no sea GNU, por ejemplo

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

en la versión no GNU tendrá que ser

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

Sin embargo, esta versión no funcionará con gnu sed.

Aquí hay una versión que funciona con ambos:

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

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
MikhailVS
fuente
De hecho, probé trabajando en Ubuntu Linux v16 y FreeBSD v10.2. Gracias.
Sopalajo de Arrierez
12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

Cómo funciona esta secuencia de comandos: para las líneas entre 1 y la primera #include(después de la línea 1), si la línea comienza con #include, entonces anteponga la línea especificada.

Sin embargo, si el primero #includeestá en la línea 1, tanto la línea 1 como la siguiente #includetendrán la línea antepuesta. Si está utilizando GNU sed, tiene una extensión donde 0,/^#include/(en lugar de 1,) hará lo correcto.

Chris Jester-Young
fuente
11

Simplemente agregue el número de ocurrencias al final:

sed s/#include/#include "newfile.h"\n#include/1
inexistente
fuente
77
Por desgracia, esto no funciona. Reemplaza la primera aparición en cada línea del archivo y no la primera aparición en el archivo.
David Dibben
1
Además, es una extensión sed de GNU, no una característica sed estándar.
Jonathan Leffler
10
Hmmm ... el tiempo pasa. POSIX 2008/2013 para sedespecifica el comando sustituto con: [2addr]s/BRE/replacement/flagsy observa que "El valor de las banderas será cero o más de: n Sustituye la enésima aparición solo del BRE encontrado dentro del espacio del patrón". Por lo tanto, al menos en POSIX 2008, el final 1no es una sedextensión de GNU . De hecho, incluso en el estándar SUS / POSIX 1997 , esto fue compatible, por lo que estaba muy fuera de lugar en 2008.
Jonathan Leffler
7

Una posible solución:

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

Explicación:

  • lee las líneas hasta encontrar el #include, imprime estas líneas y luego comienza un nuevo ciclo
  • inserte la nueva línea de inclusión
  • ingrese un bucle que solo lea líneas (de forma predeterminada, sed también imprimirá estas líneas), no volveremos a la primera parte del script desde aquí
mitchnull
fuente
sed: file me4.sed line 4: ":" lacks a label
rogerdpack
aparentemente algo cambió en las versiones sed recientes y las etiquetas vacías ya no están permitidas. actualizó la respuesta
mitchnull
4

Sé que esta es una publicación antigua pero tenía una solución que solía usar:

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

Básicamente use grep para imprimir la primera aparición y deténgase allí. Además, imprima el número de línea, es decir5:line . Canalice eso en sed y elimine el: y cualquier cosa posterior para que le quede un número de línea. Canalice eso en sed que agrega s /.*/ reemplazar al número final, lo que resulta en un script de 1 línea que se canaliza en el último sed para ejecutarse como un script en el archivo.

entonces, si regex = #includey replace = blahy la primera aparición que grep encuentra está en la línea 5, entonces los datos canalizados al último sed estarían 5s/.*/blah/.

Funciona incluso si la primera aparición está en la primera línea.

Michael Edwards
fuente
Odio por completo las secuencias de comandos sed multilínea o los comandos sed con cualquier cosa menos sy un número de lino, así que estoy de acuerdo con este enfoque. Esto es lo que usé para mi caso de uso (funciona con 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"
parity3
Funciona. Aunque solo con sed que son lo suficientemente inteligentes como para aceptar sed -f -algunos que no lo son, pero puedes
solucionarlo
3

Si alguien vino aquí para reemplazar un personaje por primera vez en todas las líneas (como yo), use esto:

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

Al cambiar 1 a 2, por ejemplo, puede reemplazar todas las segundas solo en su lugar.

FatihSarigol
fuente
2
No necesita hacer todo esto, 's/a/b/'significa match a, ydo just first match for every matching line
Samveen
Estoy con Samveen Además, esto no responde la pregunta que se hace aquí . Aconsejaría eliminar esta respuesta.
Socowi
La pregunta era "primera aparición en un archivo" no "primera aparición en una línea"
Manuel Romeiro
3

Con la -zopción de GNU sed, puede procesar todo el archivo como si fuera solo una línea. De esa manera s/…/…/, a solo reemplazaría la primera coincidencia en todo el archivo. Recuerde: s/…/…/solo reemplaza la primera coincidencia en cada línea, pero con la -zopción sedtrata el archivo completo como una sola línea.

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

En el caso general, debe reescribir su expresión sed ya que el espacio del patrón ahora contiene todo el archivo en lugar de solo una línea. Algunos ejemplos:

  • s/text.*//puede reescribirse como s/text[^\n]*//. [^\n]coincide con todo excepto el carácter de nueva línea.[^\n]*coincidirá con todos los símbolos texthasta que se llegue a una nueva línea.
  • s/^text// puede reescribirse como s/(^|\n)text//.
  • s/text$//puede reescribirse como s/text(\n|$)//.
Socowi
fuente
2

Haría esto con un script awk:

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

luego ejecútalo con awk:

awk -f awkscript headerfile.h > headerfilenew.h

podría ser descuidado, soy nuevo en esto.

wakingrufus
fuente
2

Como sugerencia alternativa, es posible que desee ver el edcomando.

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
fuente
2

Finalmente conseguí que esto funcionara en un script Bash utilizado para insertar una marca de tiempo única en cada elemento en un feed RSS:

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

Solo cambia la primera aparición.

${nowms}es el tiempo en milisegundos establecido por un script Perl, $counteres un contador utilizado para el control de bucle dentro del script, \permite que el comando continúe en la siguiente línea.

El archivo se lee y stdout se redirige a un archivo de trabajo.

Según tengo entendido, 1,/====RSSpermalink====/le dice a sed cuándo detenerse al establecer una limitación de rango, y luego s/====RSSpermalink====/${nowms}/es el comando familiar sed para reemplazar la primera cadena por la segunda.

En mi caso, pongo el comando entre comillas dobles porque lo estoy usando en un script Bash con variables.

Michael Cook
fuente
2

Usando FreeBSD ed y evita edel error "no match" en caso de que no haya una includedeclaración en un archivo para ser procesado:

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
fuente
Esto es notablemente similar a la respuesta de timo , pero se agregó más de un año después.
Jonathan Leffler
2

Esto podría funcionar para usted (GNU sed):

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

o si la memoria no es un problema:

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

El siguiente comando elimina la primera aparición de una cadena, dentro de un archivo. También elimina la línea vacía. Se presenta en un archivo xml, pero funcionaría con cualquier archivo.

Útil si trabaja con archivos xml y desea eliminar una etiqueta. En este ejemplo, elimina la primera aparición de la etiqueta "isTag".

Mando:

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

Archivo fuente (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>

Archivo de resultados (output.txt)

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

ps: no funcionó para mí en Solaris SunOS 5.10 (bastante antiguo), pero funciona en Linux 2.6, sed versión 4.1.5

Andreas Panagiotidis
fuente
Esto se parece notablemente a la misma idea básica que varias respuestas anteriores, con la misma advertencia de que solo funciona con GNU sed(por lo tanto, no funcionó con Solaris). Debe eliminar esto, por favor, realmente no proporciona información nueva distintiva a una pregunta que ya tenía 4 años y medio cuando respondió. Por supuesto, tiene un ejemplo trabajado, pero tiene un valor discutible cuando la pregunta tiene tantas respuestas como esta.
Jonathan Leffler
0

Nada nuevo, pero quizás una respuesta un poco más concreta: sed -rn '0,/foo(bar).*/ s%%\1%p'

Ejemplo: xwininfo -name unity-launcherproduce resultados como:

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

Extraer ID de ventana con xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'produce:

0x2200003
Stephen Niedzielski
fuente
0

POSIXly (también válido en sed), solo se usa una expresión regular, necesita memoria solo para una línea (como de costumbre):

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

Explicado:

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.
Isaac
fuente
0

El caso de uso puede ser que sus ocurrencias se extiendan por todo el archivo, pero sabe que su única preocupación es en las primeras 10, 20 o 100 líneas.

Luego, simplemente abordar esas líneas soluciona el problema , incluso si la redacción del OP solo se refiere primero.

sed '1,10s/#include/#include "newfile.h"\n#include/'
sastorsl
fuente
0

Una posible solución aquí podría ser decirle al compilador que incluya el encabezado sin que se mencione en los archivos de origen. EN GCC hay estas opciones:

   -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.

El compilador de Microsoft tiene el / FI opción (inclusión forzada).

Esta característica puede ser útil para algunos encabezados comunes, como la configuración de la plataforma. El Makefile del kernel de Linux utiliza -includepara esto.

Kaz
fuente
-1
sed -e 's/pattern/REPLACEMENT/1' <INPUTFILE
warhansen
fuente