Comment utiliser sed pour remplacer uniquement la première occurrence dans un fichier?

217

Je voudrais mettre à jour un grand nombre de fichiers source C ++ avec une directive include supplémentaire avant tout #includes existant. Pour ce genre de tâche, j'utilise normalement un petit script bash avec sed pour réécrire le fichier.

Comment puis-je sedremplacer uniquement la première occurrence d'une chaîne dans un fichier plutôt que de remplacer chaque occurrence?

Si j'utilise

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

il remplace tous les #includes.

Des suggestions alternatives pour réaliser la même chose sont également les bienvenues.

David Dibben
la source

Réponses:

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

ou, si vous préférez: Note de l'éditeur: fonctionne uniquement avec GNU sed .

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

La source

Ben Hoffstein
la source
86
Je pense que je préfère la solution «ou si vous préférez». Il serait également bon d'expliquer les réponses - et de faire en sorte que la réponse réponde directement à la question, puis de généraliser, plutôt que de généraliser uniquement. Mais bonne réponse.
Jonathan Leffler
7
Pour info pour les utilisateurs de Mac, vous devez remplacer le 0 par un 1, donc: fichier sed '1, / RE / s // to_that /'
mhost
1
@mhost Non, il remplacera si le motif est trouvé sur la ligne n ° 1 et non pas si le motif se trouve sur une autre ligne, mais est toujours le premier motif trouvé. PS il convient de mentionner que cela 0,ne fonctionne qu'avecgnu sed
Jotne
11
Quelqu'un pourrait-il expliquer la solution «ou si vous préférez»? Je ne sais pas où mettre le motif "de".
Jean-Luc Nacif Coelho
3
@ Jean-LucNacifCoelho: l'utilisation s//- c'est-à-dire une expression régulière vide - signifie que l' expression régulière la plus récemment appliquée est implicitement réutilisée; dans ce cas RE,. Ce raccourci pratique signifie que vous n'avez pas à dupliquer l'expression régulière de fin de plage dans votre sappel.
mklement0
289

Un sedscript qui ne remplacera que la première occurrence de "Apple" par "Banana"

Exemple

     Input:      Output:

     Apple       Banana
     Apple       Apple
     Orange      Orange
     Apple       Apple

Voici le script simple: Note de l'éditeur: fonctionne uniquement avec GNU sed .

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

Les deux premiers paramètres 0et /Apple/sont le spécificateur de plage. C'est s/Apple/Banana/ce qui est exécuté dans cette plage. Donc dans ce cas "dans la plage du début ( 0) jusqu'à la première instance de Apple, remplacez Applepar Banana. Seul le premier Applesera remplacé.

Contexte: En traditionnel, sedle spécificateur de plage est également "commencer ici" et "terminer ici" (inclus). Cependant, le "début" le plus bas est la première ligne (ligne 1), et si la "fin ici" est une expression régulière, alors il est uniquement tenté de faire correspondre la ligne suivante après le "début", donc la fin la plus précoce possible est la ligne 2. Donc, comme la plage est inclusive, la plus petite plage possible est "2 lignes" et la plus petite plage de départ est à la fois les lignes 1 et 2 (c'est-à-dire s'il y a une occurrence sur la ligne 1, les occurrences sur la ligne 2 seront également modifiées, ce qui n'est pas souhaité dans ce cas). ). GNUsed ajoute sa propre extension permettant de spécifier start comme "pseudo" line 0afin que la fin de la plage puisse être line 1, lui permettant une plage de "seulement la première ligne"

Ou une version simplifiée (un RE comme un //moyen vide pour réutiliser celui spécifié avant, donc c'est équivalent):

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

Et les accolades sont facultatives pour la scommande, c'est donc également équivalent:

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

Tous ces éléments fonctionnent seduniquement sur GNU .

Vous pouvez également installer GNU sed sur OS X en utilisant homebrew brew install gnu-sed.

Tim
la source
166
traduit en langage humain: commencez à la ligne 0, continuez jusqu'à ce que vous trouviez «Apple», exécutez la substitution entre accolades. cfr: grymoire.com/Unix/Sed.html#uh-29
mariotomo
8
Sur OS X, je reçoissed: 1: "…": bad flag in substitute command: '}'
ELLIOTTCABLE
5
@ELLIOTTCABLE sur OS X, utilisez sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. De la réponse de @ MikhailVS (actuellement) en bas ci-dessous.
djb
8
Fonctionne également sans les supports:sed '0,/foo/s/foo/bar/'
Innokenty
5
Je reçois sed: -e expression #1, char 3: unexpected '', avec
Jonathan
56
sed '0,/pattern/s/pattern/replacement/' filename

cela a fonctionné pour moi.

exemple

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

Note de l'éditeur: les deux fonctionnent uniquement avec GNU sed .

Sushil
la source
2
@Landys cela remplace toujours les instances dans les autres lignes aussi; pas seulement la première instance
sarat
1
@sarat Oui, vous avez raison. sed '1,/pattern/s/pattern/replacement/' filenamene fonctionne que si "le motif ne se produira pas sur la première ligne" sur Mac. Je vais supprimer mon commentaire précédent car il n'est pas exact. Le détail peut être trouvé ici ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). La réponse d'Andy ne fonctionne que pour GNU sed, mais pas celle sur Mac.
Landys
45

Un aperçu des nombreuses réponses utiles existantes , complété par des explications :

Les exemples ici utilisent un cas d'utilisation simplifié: remplacez le mot «foo» par «bar» dans la première ligne correspondante uniquement.
En raison de l' utilisation de la norme ANSI chaînes C-cités ( $'...') pour fournir des lignes d'entrée échantillon, bash, ksh, ou zshest supposé que la coquille.


GNU sed uniquement:

La réponse de Ben Hoffstein nous montre que GNU fournit une extension à la spécification POSIX car ellesed permet la forme à 2 adresses suivante : 0,/re/( rereprésente ici une expression régulière arbitraire).

0,/re/permet également à l'expression régulière de correspondre sur la toute première ligne . En d'autres termes: une telle adresse créera une plage de la 1ère ligne jusqu'à et y compris la ligne qui correspond re- qu'elle rese produise sur la 1ère ligne ou sur toute ligne suivante.

  • Comparez cela avec le formulaire compatible POSIX 1,/re/, qui crée une plage qui correspond de la 1ère ligne jusqu'à et y compris la ligne qui correspond resur les lignes suivantes ; en d'autres termes: cela ne détectera pas la première occurrence d'une recorrespondance s'il se produit sur la 1ère ligne et empêche// également l'utilisation de sténographie pour la réutilisation de l'expression régulière la plus récemment utilisée (voir point suivant). 1

Si vous combinez une 0,/re/adresse avec un s/.../.../appel (substitution) qui utilise la même expression régulière, votre commande n'effectuera effectivement la substitution que sur la première ligne correspondante re.
sedfournit un moyen pratique raccourci pour réutiliser l'expression régulière plus récemment appliquée : un vide paire delimiter,// .

$ 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-fonctionnalités uniquement sedtelles que BSD (macOS)sed (fonctionnera également avec GNU sed ):

Puisqu'il 0,/re/ne peut pas être utilisé et que le formulaire 1,/re/ne détectera pas res'il se produit sur la toute première ligne (voir ci-dessus), une gestion spéciale pour la 1ère ligne est requise .

La réponse de MikhailVS mentionne la technique, mise dans un exemple concret ici:

$ 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

Remarque:

  • Le //raccourci d' expression régulière vide est utilisé deux fois ici: une fois pour le point d'extrémité de la plage, et une fois dans l' sappel; dans les deux cas, l'expression régulière fooest implicitement réutilisée, ce qui nous permet de ne pas avoir à la dupliquer, ce qui rend le code plus court et plus facile à maintenir.

  • POSIX a sedbesoin de nouvelles lignes après certaines fonctions, comme le nom d'une étiquette ou même son omission, comme c'est le cas tici; Le fractionnement stratégique du script en plusieurs -eoptions est une alternative à l'utilisation d'un retour à la ligne réel: terminez chaque -ebloc de script là où un retour à la ligne devrait normalement aller.

1 s/foo/bar/remplace foouniquement sur la 1ère ligne, s'il y est trouvé. Si c'est le cas, se tramifie à la fin du script (ignore les commandes restantes sur la ligne). (La tfonction se branche sur une étiquette uniquement si l' sappel le plus récent a effectué une substitution réelle; en l'absence d'une étiquette, comme c'est le cas ici, la fin du script est branchée sur).

Lorsque cela se produit, l'adresse de plage 1,//, qui trouve normalement la première occurrence à partir de la ligne 2 , ne correspondra pas et la plage ne sera pas traitée, car l'adresse est évaluée lorsque la ligne actuelle est déjà 2.

Inversement, s'il n'y a pas de correspondance sur la 1ère ligne, 1,// sera entré et trouvera la vraie première correspondance.

L'effet net est le même que celui avec GNU sedde 0,/re/: seule la première occurrence est remplacée, si elle se produit sur la 1ère ligne ou tout autre.


Approches sans portée

la réponse de potong démontre des techniques de boucle qui contournent le besoin d'une gamme ; puisqu'il utilise la syntaxe GNU sed , voici les équivalents compatibles POSIX :

Technique de boucle 1: lors de la première correspondance, effectuez la substitution, puis entrez une boucle qui imprime simplement les lignes restantes telles quelles :

$ 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

Technique de boucle 2, pour les petits fichiers uniquement : lisez l'intégralité de l'entrée en mémoire, puis effectuez une seule substitution dessus .

$ 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 donne des exemples de ce qui se passe avec 1,/re/, avec et sans suite s//:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'rendements $'1bar\n2bar'; c'est-à-dire que les deux lignes ont été mises à jour, car le numéro de ligne 1correspond à la 1ère ligne, et l'expression régulière /foo/- la fin de la plage - n'est alors recherchée qu'à partir de la ligne suivante . Par conséquent, les deux lignes sont sélectionnées dans ce cas et la s/foo/bar/substitution est effectuée sur les deux.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo' échoue : avec sed: first RE may not be empty(BSD / macOS) et sed: -e expression #1, char 0: no previous regular expression(GNU), car, au moment où la 1ère ligne est traitée (en raison du numéro de ligne 1commençant la plage), aucune expression régulière n'a encore été appliquée, donc//ne fait référence à rien.
À l'exception de sedla 0,/re/syntaxe spéciale de GNU , toute plage commençant par un numéro de ligne empêche effectivement l'utilisation de //.

mklement0
la source
25

Vous pouvez utiliser awk pour faire quelque chose de similaire ..

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

Explication:

/#include/ && !done

Exécute l'instruction d'action entre {} lorsque la ligne correspond à "#include" et que nous ne l'avons pas déjà traitée.

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

Cela affiche #include "newfile.h", nous devons échapper aux guillemets. Ensuite, nous définissons la variable done sur 1, donc nous n'ajoutons pas plus d'inclusions.

1;

Cela signifie "imprimer la ligne" - une action vide par défaut imprime $ 0, qui imprime la ligne entière. Un liner et plus facile à comprendre que sed IMO :-)

richq
la source
4
Cette réponse est plus portable que les solutions sed, qui reposent sur gnu sed etc. (par exemple sed dans OS-X craint!)
Jay Taylor
1
C'est vraiment plus compréhensible, mais pour moi, cela ajoute une ligne au lieu de la remplacer; commande utilisée: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
Cereal Killer
même ici, la commande la plus compréhensible, mais elle ajoute une ligne au-dessus de la chaîne trouvée au lieu de la remplacer
Flion
1
La réponse est assez lisible. Voici ma version qui remplace la chaîne au lieu d'ajouter une nouvelle ligne. awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Orsiris de Jong
18

Une collection assez complète de réponses sur la FAQ linuxtopia sed . Il souligne également que certaines réponses fournies par les personnes ne fonctionneront pas avec la version non GNU de sed, par exemple

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

en version non GNU devra être

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

Cependant, cette version ne fonctionnera pas avec gnu sed.

Voici une version qui fonctionne avec les deux:

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

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
MikhailVS
la source
En effet, testé sur Ubuntu Linux v16 et FreeBSD v10.2. Merci.
Sopalajo de Arrierez
12
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

Fonctionnement de ce script: pour les lignes comprises entre 1 et la première #include(après la ligne 1), si la ligne commence par #include, ajoutez la ligne spécifiée au début .

Cependant, si le premier #includeest dans la ligne 1, la ligne 1 et la suivante suivante #includeauront la ligne ajoutée. Si vous utilisez GNU sed, il a une extension où 0,/^#include/(au lieu de 1,) fera la bonne chose.

Chris Jester-Young
la source
11

Ajoutez simplement le nombre d'occurrences à la fin:

sed s/#include/#include "newfile.h"\n#include/1
ne pas exister
la source
7
Malheureusement ça ne fonctionne pas. Il remplace la première occurrence juste sur chaque ligne du fichier et non la première occurrence dans le fichier.
David Dibben
1
De plus, il s'agit d'une extension GNU sed, et non d'une fonctionnalité sed standard.
Jonathan Leffler
10
Hmmm ... le temps passe. POSIX 2008/2013 pour sedspécifie la commande de substitution avec: [2addr]s/BRE/replacement/flagset note que "La valeur des drapeaux doit être zéro ou plus de: n Ne remplace la nième occurrence que du BRE trouvé dans l'espace de modèle." Ainsi, au moins dans POSIX 2008, la fin 1n'est pas une sedextension GNU . En effet, même dans la norme SUS / POSIX 1997 , cela était pris en charge, donc j'étais très mal aligné en 2008.
Jonathan Leffler
7

Une solution possible:

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

Explication:

  • lire les lignes jusqu'à ce que nous trouvions le #include, imprimer ces lignes puis commencer un nouveau cycle
  • insérer la nouvelle ligne d'inclusion
  • entrez une boucle qui lit simplement les lignes (par défaut sed imprimera également ces lignes), nous ne reviendrons pas à la première partie du script d'ici
mitchnull
la source
sed: file me4.sed line 4: ":" lacks a label
rogerdpack
apparemment, quelque chose a changé dans les dernières versions sed et les étiquettes vides ne sont plus autorisées. a mis à jour la réponse
mitchnull le
4

Je sais que c'est un ancien poste mais j'avais une solution que j'utilisais:

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

Utilisez essentiellement grep pour imprimer la première occurrence et arrêtez-vous là. En outre, imprimer le numéro de ligne, par exemple 5:line. Canalisez cela dans sed et supprimez le: et tout ce qui se trouve après pour que vous vous retrouviez avec un numéro de ligne. Canalisez cela dans sed qui ajoute s /.*/ replace au numéro de fin, ce qui entraîne un script d'une ligne qui est canalisé dans le dernier sed pour s'exécuter en tant que script dans le fichier.

donc si regex = #includeet replace = blahet que la première occurrence de grep est sur la ligne 5, alors les données acheminées vers le dernier sed seraient 5s/.*/blah/.

Fonctionne même si la première occurrence se trouve sur la première ligne.

Michael Edwards
la source
Je déteste totalement les scripts sed multilignes ou les commandes sed avec autre chose que s et un nombre de lin donc je suis à bord avec cette approche. Voici ce que j'ai utilisé pour mon cas d'utilisation (fonctionne avec 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
Ça marche. Mais seulement avec des sed qui sont assez intelligents pour accepter ceux sed -f -qui ne le sont pas, mais vous pouvez contourner ce
problème
3

Si quelqu'un est venu ici pour remplacer un caractère pour la première occurrence dans toutes les lignes (comme moi), utilisez ceci:

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

En changeant 1 à 2 par exemple, vous pouvez remplacer uniquement tous les seconds a.

FatihSarigol
la source
2
Vous n'avez pas besoin de faire tout cela, cela 's/a/b/'signifie match a, etdo just first match for every matching line
Samveen
Je suis avec Samveen. En outre , cela ne répond pas à la question posée ici . Je conseillerais de supprimer cette réponse.
Socowi
La question était "première occurrence dans un fichier" et non "première occurrence dans une ligne"
Manuel Romeiro
3

Avec l' -zoption de GNU sed, vous pouvez traiter l'intégralité du fichier comme s'il ne s'agissait que d'une seule ligne. De cette façon, a s/…/…/ne remplacerait que la première correspondance de l'ensemble du fichier. Rappelez-vous: s/…/…/ne remplace que la première correspondance de chaque ligne, mais avec l' -zoption sedtraite le fichier entier comme une seule ligne.

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

Dans le cas général, vous devez réécrire votre expression sed car l'espace de motif contient maintenant le fichier entier au lieu d'une seule ligne. Quelques exemples:

  • s/text.*//peut être réécrit en s/text[^\n]*//. [^\n]correspond à tout sauf au caractère de nouvelle ligne. [^\n]*correspondra à tous les symboles après textjusqu'à ce qu'une nouvelle ligne soit atteinte.
  • s/^text//peut être réécrit en s/(^|\n)text//.
  • s/text$//peut être réécrit en s/text(\n|$)//.
Socowi
la source
2

je le ferais avec un script awk:

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

puis lancez-le avec awk:

awk -f awkscript headerfile.h > headerfilenew.h

pourrait être bâclé, je suis nouveau dans ce domaine.

wakingrufus
la source
2

Comme suggestion alternative, vous voudrez peut-être regarder la edcommande.

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
la source
2

J'ai finalement réussi à faire fonctionner cela dans un script Bash utilisé pour insérer un horodatage unique dans chaque élément d'un flux RSS:

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

Il modifie uniquement la première occurrence.

${nowms}est le temps en millisecondes défini par un script Perl, $counterest un compteur utilisé pour le contrôle de boucle dans le script, \permet de poursuivre la commande sur la ligne suivante.

Le fichier est lu et stdout est redirigé vers un fichier de travail.

La façon dont je le comprends, 1,/====RSSpermalink====/indique à sed quand s'arrêter en définissant une limitation de plage, puis s/====RSSpermalink====/${nowms}/est la commande familière sed pour remplacer la première chaîne par la seconde.

Dans mon cas, j'ai mis la commande entre guillemets doubles car je l'utilise dans un script Bash avec des variables.

Michael Cook
la source
2

Utiliser FreeBSD ed et éviter edl'erreur "pas de correspondance" au cas où il n'y aurait pas de includedéclaration dans un fichier à traiter:

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
la source
Ceci est remarquablement similaire à la réponse de timo mais a été ajouté plus d'un an plus tard.
Jonathan Leffler
2

Cela pourrait fonctionner pour vous (GNU sed):

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

ou si la mémoire n'est pas un problème:

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

La commande suivante supprime la première occurrence d'une chaîne, dans un fichier. Il supprime également la ligne vide. Il est présenté sur un fichier xml, mais il fonctionnerait avec n'importe quel fichier.

Utile si vous travaillez avec des fichiers xml et que vous souhaitez supprimer une balise. Dans cet exemple, il supprime la première occurrence de la balise "isTag".

Commander:

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

Fichier source (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>

Fichier de résultats (output.txt)

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

ps: cela ne fonctionnait pas pour moi sur Solaris SunOS 5.10 (assez ancien), mais cela fonctionne sur Linux 2.6, sed version 4.1.5

Andreas Panagiotidis
la source
Cela ressemble remarquablement à la même idée de base qu'un certain nombre de réponses précédentes, avec la même mise en garde que cela ne fonctionne qu'avec GNU sed(donc cela ne fonctionnait pas avec Solaris). Vous devriez supprimer ceci, s'il vous plaît - il ne fournit vraiment pas de nouvelles informations distinctives à une question qui avait déjà 4 ans et demi lorsque vous avez répondu. Certes, il a un exemple concret, mais c'est d'une valeur discutable lorsque la question a autant de réponses que celle-ci.
Jonathan Leffler
0

Rien de nouveau mais peut-être une réponse un peu plus concrète: sed -rn '0,/foo(bar).*/ s%%\1%p'

Exemple: xwininfo -name unity-launcherproduit une sortie comme:

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

Extraire l'ID de fenêtre avec xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p'produit:

0x2200003
Stephen Niedzielski
la source
0

POSIXly (également valable dans sed), une seule expression régulière utilisée, ne nécessite de la mémoire que pour une ligne (comme d'habitude):

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

Expliqué:

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
la source
0

Le cas d'utilisation peut peut-être être que vos occurrences sont réparties dans tout votre fichier, mais vous savez que votre seule préoccupation est dans les 10, 20 ou 100 premières lignes.

Le simple fait d'adresser ces lignes résout le problème - même si le libellé du PO ne concerne que le premier.

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

Une solution possible ici pourrait être de dire au compilateur d'inclure l'en-tête sans qu'il soit mentionné dans les fichiers source. DANS GCC, il y a ces options:

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

Le compilateur de Microsoft a l' option / FI (inclusion forcée).

Cette fonctionnalité peut être pratique pour certains en-têtes communs, comme la configuration de la plate-forme. Le Makefile du noyau Linux utilise -includepour cela.

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