Come usare sed per sostituire solo la prima occorrenza in un file?


217

Vorrei aggiornare un gran numero di file sorgente C ++ con una direttiva di inclusione aggiuntiva prima di qualsiasi #includes esistente. Per questo tipo di attività, normalmente utilizzo un piccolo script bash con sed per riscrivere il file.

Come posso sedsostituire solo la prima occorrenza di una stringa in un file anziché sostituire ogni occorrenza?

Se io uso

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

sostituisce tutti i #include.

Sono anche ben accetti suggerimenti alternativi per ottenere lo stesso risultato.

Risposte:


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

oppure, se preferisci: Nota del redattore: funziona solo con GNU sed .

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

fonte


86
Penso di preferire la soluzione "o se preferisci". Sarebbe anche utile spiegare le risposte - e fare in modo che la risposta affronti direttamente la domanda e quindi generalizzi, piuttosto che generalizzare solo. Ma buona risposta
Jonathan Leffler,

7
Cordiali saluti per gli utenti Mac, è necessario sostituire lo 0 con un 1, quindi: sed '1, / RE / s // to_that /' file
mhost

1
@mhost No, sarà sufficiente sostituire se il modello si trova nella riga n. 1 e non se il modello si trova su un'altra riga, ma rimane comunque il primo modello trovato. PS, va detto che 0,funziona solo congnu sed
Jotne,

11
Qualcuno potrebbe spiegare la soluzione "o se prefeer"? Non so dove mettere il modello "from".
Jean-Luc Nacif Coelho,

3
@ Jean-LucNacifCoelho: usare s//- cioè un regex vuoto - significa che il regex applicato più di recente viene implicitamente riutilizzato; in questo caso RE,. Questo comodo collegamento significa che non è necessario duplicare la regex di fine intervallo nella schiamata.
mklement0

290

Uno sedscript che sostituirà solo la prima occorrenza di "Apple" con "Banana"

Esempio

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

Questo è il semplice script: Nota del redattore: funziona solo con GNU sed .

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

I primi due parametri 0e /Apple/sono l'identificatore di intervallo. Il s/Apple/Banana/è quello che viene eseguito all'interno di tale intervallo. Quindi in questo caso "entro l'intervallo di inizio ( 0) fino alla prima istanza di Apple, sostituisci Applecon Banana. AppleVerrà sostituito solo il primo .

Contesto: nel tradizionale sedlo specificatore di intervallo è anche "inizia qui" e "termina qui" (incluso). Comunque il "inizio" più basso è la prima riga (riga 1), e se la "fine qui" è una regex, allora si tenta di trovare una corrispondenza solo sulla riga successiva dopo "inizio", quindi la prima fine possibile è la riga 2. Pertanto, poiché l'intervallo è inclusivo, l'intervallo più piccolo possibile è "2 righe" e l'intervallo iniziale più piccolo è sia le righe 1 che 2 (ovvero se si verifica un'occorrenza sulla riga 1, anche le occorrenze sulla riga 2 verranno modificate, non desiderato in questo caso ). GNUsed aggiunge la propria estensione per consentire di specificare l'inizio come "pseudo" in line 0modo che la fine dell'intervallo possa essere line 1, consentendogli un intervallo di "solo la prima riga"

O una versione semplificata (una RE vuota //vuol dire riutilizzare quella specificata prima, quindi questo è equivalente):

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

E le parentesi graffe sono opzionali per il scomando, quindi anche questo è equivalente:

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

Tutti questi funzionano sedsolo su GNU .

Puoi anche installare GNU sed su OS X usando homebrew brew install gnu-sed.


167
tradotto in linguaggio umano: inizia dalla riga 0, continua fino a quando non abbini "Apple", esegui la sostituzione tra parentesi graffe. cfr: grymoire.com/Unix/Sed.html#uh-29
mariotomo

8
Su OS X, ottengosed: 1: "…": bad flag in substitute command: '}'
ELLIOTTCABLE

5
@ELLIOTTCABLE su OS X, utilizzare sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. Dalla risposta di @ MikhailVS (attualmente) molto in basso.
DJ

8
Funziona anche senza parentesi quadre:sed '0,/foo/s/foo/bar/'
Innokenty

5
Ricevo sed: -e expression #1, char 3: unexpected '' con questo
Jonathan il

56
sed '0,/pattern/s/pattern/replacement/' filename

questo ha funzionato per me.

esempio

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

Nota del redattore: entrambi funzionano solo con GNU sed .


2
@Landys sostituisce comunque anche le istanze nelle altre righe; non solo la prima istanza
sarat l'

1
@sarat Sì, hai ragione. sed '1,/pattern/s/pattern/replacement/' filenamefunziona solo se "il modello non si verifica sulla prima riga" su Mac. Eliminerò il mio commento precedente poiché non è preciso. I dettagli sono disponibili qui ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). La risposta di Andy funziona solo per GNU sed, ma non quella su Mac.
Landys,

45

Una panoramica delle molte utili risposte esistenti , integrate da spiegazioni :

Gli esempi qui usano un caso d'uso semplificato: sostituisci la parola "pippo" con "barra" solo nella prima riga corrispondente.
Grazie all'uso di stringhe ANSI C-quotati ( $'...') per fornire le linee di ingresso del campione, bash, ksh, o zshviene assunto come shell.


sedSolo GNU :

La risposta di Ben Hoffstein ci mostra che GNU fornisce un'estensione alla specifica POSIXsed che consente il seguente modulo a 2 indirizzi : 0,/re/( rerappresenta qui un'espressione regolare arbitraria).

0,/re/consente alla regex di corrispondere anche sulla prima riga . In altre parole: un tale indirizzo creerà un intervallo dalla 1a riga fino alla riga corrispondente, compresa rese si reverifica sulla 1a riga o su qualsiasi riga successiva.

  • In contrasto con la forma POSIX-compliant 1,/re/, che crea una gamma che partite dal 1 ° linea fino ad includere la linea che le partite resu successive linee; in altre parole: questo non rileverà la prima occorrenza di una recorrispondenza se si verifica sulla prima riga e impedisce// anche l'uso della scorciatoia per il riutilizzo dell'ultima regex utilizzata (vedere il punto successivo). 1

Se si combina un 0,/re/indirizzo con una s/.../.../chiamata (sostituzione) che utilizza la stessa espressione regolare, il comando eseguirà effettivamente la sostituzione solo sulla prima riga corrispondente re.
sedfornisce una comoda scorciatoia per riutilizzare l'espressione regolare più recentemente applicato : un vuoto coppia delimitatore// .

$ 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

Solo funzionalità POSIX sedcome BSD (macOS)sed (funzionerà anche con GNU sed ):

Poiché 0,/re/non può essere utilizzato e il modulo 1,/re/non rileverà rese si verifica nella prima riga (vedere sopra), è richiesta una gestione speciale per la prima riga .

La risposta di MikhailVS menziona la tecnica, messa qui in un esempio concreto:

$ 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:

  • Il //collegamento regex vuoto viene impiegato due volte qui: una volta per l'endpoint dell'intervallo e una volta nella schiamata; in entrambi i casi, regex fooviene riutilizzato implicitamente, permettendoci di non doverlo duplicare, il che rende il codice più breve e più gestibile.

  • POSIX sednecessita di nuove righe effettive dopo determinate funzioni, come ad esempio il nome di un'etichetta o addirittura la sua omissione, come nel caso tqui; La suddivisione strategica dello script in più -eopzioni è un'alternativa all'utilizzo di -euna nuova riga effettiva: termina ogni blocco di script dove normalmente dovrebbe andare una nuova riga.

1 s/foo/bar/sostituisce solo foosulla prima riga, se presente. In tal caso, si tramifica alla fine dello script (salta i comandi rimanenti sulla riga). (La tfunzione si ramifica su un'etichetta solo se la schiamata più recente ha eseguito una sostituzione effettiva; in assenza di un'etichetta, come nel caso qui, la fine dello script è ramificata a).

Quando ciò accade, l'indirizzo di intervallo 1,//, che normalmente trova la prima occorrenza a partire dalla riga 2 , non corrisponderà e l'intervallo non verrà elaborato, poiché l'indirizzo viene valutato quando la riga corrente è già 2.

Viceversa, se non c'è corrispondenza sulla prima riga, 1,// verrà inserito e troverà la prima vera corrispondenza.

L'effetto netto è la stessa con GNU seds' 0,/re/: solo la prima occorrenza è sostituita, se si verifica sulla linea 1 o qualsiasi altro.


Approcci non di portata

la risposta di potong dimostra tecniche di loop che aggirano la necessità di un intervallo ; poiché usa la sintassi GNU sed , ecco gli equivalenti compatibili con POSIX :

Tecnica del loop 1: al primo incontro, esegui la sostituzione, quindi inserisci un loop che stampa semplicemente le righe rimanenti così come sono :

$ 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

Loop tecnica 2, solo per file di piccole dimensioni : leggi l'intero input in memoria, quindi esegui una singola sostituzione su di esso .

$ 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 fornisce esempi di ciò che accade 1,/re/, con e senza un successivo s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendimenti $'1bar\n2bar'; cioè, entrambe le linee sono state aggiornate, perché il numero di riga 1corrisponde alla prima riga e regex /foo/- la fine dell'intervallo - viene quindi cercato solo a partire dalla riga successiva . Pertanto, entrambe le linee vengono selezionate in questo caso e la s/foo/bar/sostituzione viene eseguita su entrambe.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' fallisce : con sed: first RE may not be empty(BSD / macOS) e sed: -e expression #1, char 0: no previous regular expression(GNU), perché, al momento dell'elaborazione della prima riga (a causa del numero di riga che 1inizia l'intervallo), non è stato ancora applicato alcun regex, quindi//non si riferisce a nulla.
Ad eccezione della sedspeciale 0,/re/sintassi di GNU , qualsiasi intervallo che inizia con un numero di riga ne preclude effettivamente l'uso //.


25

Puoi usare awk per fare qualcosa di simile ..

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

Spiegazione:

/#include/ && !done

Esegue l'istruzione action tra {} quando la riga corrisponde a "#include" e non l'abbiamo già elaborata.

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

Questo stampa #include "newfile.h", dobbiamo evitare le virgolette. Quindi impostiamo la variabile done su 1, quindi non aggiungiamo più inclusioni.

1;

Ciò significa "stampa la riga": per impostazione predefinita, un'azione vuota stampa $ 0, che stampa l'intera riga. Una fodera e più facile da capire rispetto a sed IMO :-)


4
Questa risposta è più portatile delle soluzioni sed, che si basano su gnu sed ecc. (Es. Sed in OS-X fa schifo!)
Jay Taylor,

1
Questo è veramente più comprensibile, ma per me aggiunge una linea invece di sostituirla; comando usato: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
Cereal Killer

stesso qui, il comando più comprensibile, ma aggiunge una riga sopra la stringa trovata invece di sostituirla
Flion

1
La risposta è abbastanza leggibile. Ecco la mia versione che sostituisce la stringa invece di aggiungere una nuova riga. awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Orsiris de Jong,

18

Una raccolta completa di risposte alle domande frequenti su linuxtopia sed . Sottolinea inoltre che alcune risposte fornite dalle persone non funzionano con la versione non GNU di sed, ad es

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

in versione non GNU dovrà essere

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

Tuttavia, questa versione non funziona con gnu sed.

Ecco una versione che funziona con entrambi:

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

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename

Effettivamente, testato lavorando su Ubuntu Linux v16 e FreeBSD v10.2. Grazie.
Sopalajo de Arrierez,

12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

Come funziona questo script: per le linee tra 1 e la prima #include(dopo la linea 1), se la linea inizia con #include, quindi anteporre la linea specificata.

Tuttavia, se il primo #includeè nella riga 1, sia la riga 1 che la successiva successiva #includeavranno la riga anteposta. Se stai usando GNU sed, ha un'estensione dove 0,/^#include/(invece di 1,) farà la cosa giusta.


11

Aggiungi il numero di occorrenze alla fine:

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

7
Sfortunatamente, questo non funziona. Sostituisce la prima occorrenza su ogni riga del file e non la prima occorrenza nel file.
David Dibben,

1
Inoltre, è un'estensione GNU sed, non una funzionalità sed standard.
Jonathan Leffler,

10
Hmmm ... il tempo passa. POSIX 2008/2013 sedspecifica il comando sostitutivo con: [2addr]s/BRE/replacement/flagse nota che "Il valore dei flag deve essere zero o più di: n Sostituisce solo l'ennesima occorrenza del BRE trovato nello spazio del modello." Pertanto, almeno in POSIX 2008, il trailing 1non è sedun'estensione GNU . In effetti, anche nello standard SUS / POSIX 1997 , questo è stato supportato, quindi ero molto fuori linea nel 2008.
Jonathan Leffler

7

Una possibile soluzione:

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

Spiegazione:

  • leggere le righe fino a quando non troviamo #include, stampare queste righe e iniziare un nuovo ciclo
  • inserire la nuova linea di inclusione
  • inserire un ciclo che legge solo le righe (di default sed stampa anche queste righe), da qui non torneremo alla prima parte dello script

sed: file me4.sed line 4: ":" lacks a label
rogerdpack,

apparentemente qualcosa è cambiato nelle recenti versioni di sed e le etichette vuote non sono più consentite. aggiornata la risposta
mitchnull il

4

So che questo è un vecchio post ma avevo una soluzione che usavo:

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

Fondamentalmente usa grep per stampare la prima occorrenza e fermarti qui. Inoltre, stampare il numero di riga, ad es 5:line. Inseriscilo in sed e rimuovi il: e tutto il resto, quindi ti resta solo un numero di riga. Inseriscilo in sed che aggiunge s /.*/ sostituisci al numero finale, che si traduce in uno script di 1 riga che viene reindirizzato nell'ultimo sed per essere eseguito come uno script sul file.

quindi se regex = #includee rimpiazza = blahe la prima occorrenza trovata da grep è sulla riga 5, i dati inviati all'ultima sed sarebbero 5s/.*/blah/.

Funziona anche se la prima occorrenza è sulla prima riga.


Odio totalmente gli script sed multilinea o i comandi sed con qualsiasi cosa tranne se e un numero di lino, quindi sono d'accordo con questo approccio. Ecco cosa ho usato per il mio caso d'uso (funziona 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

Funziona. Anche se solo con sed che sono abbastanza intelligenti da accettare quelli sed -f -che non lo sono, ma puoi
aggirarlo

3

Se qualcuno è venuto qui per sostituire un personaggio per la prima occorrenza in tutte le righe (come me), usa questo:

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

Modificando 1 a 2, ad esempio, è possibile sostituire solo tutte le seconde a.


2
Non devi fare tutto questo, 's/a/b/'significa match a, edo just first match for every matching line
Samveen,

Sono con Samveen. Anche questo non risponde alla domanda posta qui . Consiglierei di eliminare questa risposta.
Socowi,

La domanda era "prima occorrenza in un file" non "prima occorrenza in una riga"
Manuel Romeiro,

3

Con l' -zopzione GNU sed puoi elaborare l'intero file come se fosse solo una riga. In questo modo a s/…/…/sostituirebbe solo la prima corrispondenza nell'intero file. Ricorda: s/…/…/sostituisce solo la prima corrispondenza in ogni riga, ma con l' -zopzione sedtratta l'intero file come una singola riga.

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

Nel caso generale devi riscrivere la tua espressione sed poiché lo spazio del modello ora contiene l'intero file invece di una sola riga. Qualche esempio:

  • s/text.*//può essere riscritto come s/text[^\n]*//. [^\n]corrisponde a tutto tranne il carattere di nuova riga. [^\n]*corrisponderà a tutti i simboli dopo textfino a quando non viene raggiunta una nuova riga.
  • s/^text//può essere riscritto come s/(^|\n)text//.
  • s/text$//può essere riscritto come s/text(\n|$)//.

2

lo farei con uno script awk:

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

quindi eseguilo con awk:

awk -f awkscript headerfile.h > headerfilenew.h

potrebbe essere sciatto, sono nuovo a questo.


2

Come suggerimento alternativo potresti voler guardare il 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

2

Finalmente ho capito che funzionava in uno script Bash usato per inserire un timestamp univoco in ogni elemento in un feed RSS:

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

Cambia solo la prima occorrenza.

${nowms}è il tempo in millisecondi impostato da uno script Perl, $counterè un contatore utilizzato per il controllo del ciclo all'interno dello script, \consente di continuare il comando sulla riga successiva.

Il file viene letto e stdout viene reindirizzato a un file di lavoro.

Il modo in cui lo capisco, 1,/====RSSpermalink====/dice a sed quando fermarsi impostando un limite di intervallo, e quindi s/====RSSpermalink====/${nowms}/è il comando sed familiare per sostituire la prima stringa con la seconda.

Nel mio caso ho messo il comando tra virgolette perché lo sto usando in uno script Bash con variabili.


2

Usare FreeBSD ed ed evitare edl'errore "nessuna corrispondenza" nel caso in cui non ci siano includeistruzioni in un file da elaborare:

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

Questo è notevolmente simile alla risposta di Timo , ma è stata aggiunta oltre un anno dopo.
Jonathan Leffler,

2

Questo potrebbe funzionare per te (GNU sed):

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

o se la memoria non è un problema:

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

0

Il seguente comando rimuove la prima occorrenza di una stringa, all'interno di un file. Rimuove anche la linea vuota. Viene presentato su un file XML, ma funzionerebbe con qualsiasi file.

Utile se lavori con file xml e vuoi rimuovere un tag. In questo esempio rimuove la prima occorrenza del tag "isTag".

Comando:

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

File di origine (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>

File dei risultati (output.txt)

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

ps: non ha funzionato per me su Solaris SunOS 5.10 (abbastanza vecchio), ma funziona su Linux 2.6, versione sed 4.1.5


Questa sembra notevolmente la stessa idea di base di un numero di risposte precedenti, con lo stesso avvertimento che funziona solo con GNU sed(quindi non ha funzionato con Solaris). Dovresti eliminarlo, per favore: in realtà non fornisce nuove informazioni distintive a una domanda che aveva già 4 anni e mezzo quando hai risposto. Certo, ha un esempio funzionante, ma è di valore discutibile quando la domanda ha tante risposte quante questa ha.
Jonathan Leffler,

0

Niente di nuovo ma forse una risposta un po 'più concreta: sed -rn '0,/foo(bar).*/ s%%\1%p'

Esempio: xwininfo -name unity-launcherproduce output come:

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

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

0x2200003

0

POSIXly (valido anche in sed), usato solo un regex, necessita di memoria solo per una riga (come al solito):

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

Ha spiegato:

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

Il caso d'uso può forse essere che le tue occorrenze siano distribuite nel tuo file, ma sai che la tua unica preoccupazione è nelle prime 10, 20 o 100 righe.

Quindi il semplice indirizzo di queste righe risolve il problema , anche se la formulazione del PO riguarda solo per prima.

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

0

Una possibile soluzione qui potrebbe essere quella di dire al compilatore di includere l'intestazione senza che sia menzionata nei file di origine. IN GCC ci sono queste opzioni:

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

Il compilatore di Microsoft ha l' opzione / FI (forzata inclusione).

Questa funzione può essere utile per alcune intestazioni comuni, come la configurazione della piattaforma. Il Makefile del kernel Linux lo usa -includeper questo.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.