Comment utiliser sed pour remplacer uniquement la première occurrence dans un fichier?
Je voudrais mettre à jour un grand nombre de fichiers source C++ avec un supplément incluent la directive avant tout #existant comprend. Pour ce genre de tâche, j'ai l'habitude d'utiliser un petit script bash avec sed pour ré-écrire le fichier.
Comment puis-je obtenir sed
de remplacer 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 solutions de rechange pour obtenir la même chose, ils sont également les bienvenus.
Vous devez vous connecter pour publier un commentaire.
ou, si vous préférez: note de l'Éditeur: fonctionne avec GNU
sed
seulement.Source
0,
ne fonctionne qu'avecgnu sed
s//
- à-d., un vide regex signifie que le plus récent regex est implicitement réutilisés; dans ce cas,RE
. Ce raccourci pratique signifie que vous n'avez pas à reproduire la gamme de fin de regex dans votres
appel.echo "abc\nabc" | sed '0,/^abc/s//ab/'
sed '0,/RE/{s//to_that/;q;}' file
/
. lorsque j'essaie d'utiliser:
au lieu de/
, j'ai eu une erreur. Et qu'est ce que0
la première fois?/RE/{s//to_that/;q}
Écrire un script sed qui ne fera que remplacer la première occurrence de "Apple" en "Banane"
Exemple Entrée: Sortie:
C'est la simple script: note de l'Éditeur: fonctionne avec GNU
sed
seulement.sed: 1: "…": bad flag in substitute command: '}'
sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'
. À partir de @MikhailVS de réponse (pour l'instant) chemin vers le bas ci-dessous.sed '0,/foo/s/foo/bar/'
sed: -e expression #1, char 3: unexpected
," avec cesed -i '|DATADIR.*=.*|{s/DATADIR.*=.*|DATADIR=/var/lib/`hostname`|1}
la Note 1 à la fin après le dernier délimiteursed -i -E "0,|(Apple).*|{s|(Apple:).*|Banana: "$BananaVariable"/data.txt|}" filename
sed '0,/White/ s/cat/dog/' filename
remplace toutes les occurrences de "chat" avec "chien" jusqu'à et y compris la première ligne avec le "chat Blanc". Vous aurez besoin de faire quelque chose de plus commesed '0,/White/ s/White cat/White dog/' filename
sed '0,/Apple/s//Banana/'
. Pas de répétition est essentielle pour des motifs complexes, par exemple, j'ai utilisé ce travail de mise à jour de la première version de la propriété:sed -i -r -e '0,/^(\s*<property +name="version" +value=")[^"]+/s//\11.0.0/' build.xml
cela a fonctionné pour moi.
exemple
note de l'Éditeur: les deux fonctionnent avec GNU
sed
seulement.sed '1,/pattern/s/pattern/replacement/' filename
ne fonctionne que si "le modèle n'aura pas lieu sur la première ligne" sur Mac. Je vais supprimer mon commentaire précédent puisqu'il n'est pas exact. Les détails peuvent être trouvés ici (linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/...). Andy réponse ne fonctionne que pour GNU sed, mais pas l'un sur Mac.Un aperçu de l'utile beaucoup les réponses existantes, complété par explications:
Le ici des exemples de l'utilisation d'une procédure simplifiée de cas d'utilisation: remplacer le mot " foo " par " bar " dans la première ligne seulement.
En raison de l'utilisation de C ANSI-chaînes entre guillemets (
$'...'
) de fournir l'échantillon, les lignes d'entrée,bash
,ksh
, ouzsh
est supposé que le shell.GNU
sed
seulement:Ben Hoffstein de anwswer nous montre que GNU fournit une extension à la Spécification POSIX pour
sed
qui permet à l'2-formulaire d'adresse:0,/re/
(re
représente un arbitraire expression régulière ici).0,/re/
permet l'expression régulière regex pour match sur la première ligne également. En d'autres termes: cette adresse permettra de créer une gamme à partir de la 1ère ligne de jusqu'à et y compris la ligne qui correspond àre
- sire
se produit sur la 1ère ligne ou sur les éventuels ligne.1,/re/
, ce qui crée une gamme qui correspond à compter de la 1ère ligne de jusqu'à et y compris la ligne qui correspond àre
sur ultérieure lignes; en d'autres termes: ce ne détecte pas la première occurrence d'unere
match si il arrive à se produire sur la 1er ligne et aussi empêche l'utilisation de la sténographie de//
pour réutiliser le plus récemment utilisé les regex (voir point suivant).[1]Si vous combinez une
0,/re/
adresse avec uns/.../.../
(substitution) appel qui utilise le même expression régulière, votre commande sera efficacement que d'effectuer la substitution sur le première ligne qui correspond àre
.sed
fournit un moyen pratique raccourci pour les réutiliser le plus récent expression régulière: un vide délimiteur paire,//
.Une POSIX-caractéristiques-seulement
sed
comme BSD (macOS)sed
(collaborera également avec GNUsed
):Depuis
0,/re/
ne peuvent pas être utilisés et la forme1,/re/
ne détecte pasre
si il arrive à se produire sur la première ligne (voir ci-dessus), un traitement spécial pour la 1ère ligne est nécessaire.MikhailVS réponse mentionne la technique, de la mettre dans un exemple concret ici:
Remarque:
Le vide regex
//
raccourci est employé deux fois ici: une fois pour le point de terminaison de la gamme, et une fois dans las
appel; dans les deux cas, la regexfoo
est implicitement réutilisés, ce qui nous permet de ne pas avoir à dupliquer, ce qui le rend à la fois plus courtes et plus maintenable code.POSIX
sed
besoins réels des retours à la ligne après certaines fonctions, telles que, d'après le nom d'un label ou une omission, comme c'est le cas avect
ici; stratégiquement fendre le script en plusieurs-e
options est une alternative à l'utilisation réelle des retours à la ligne: la fin de chaque-e
script morceau où un retour à la ligne aurait normalement besoin d'aller.1 s/foo/bar/
remplacefoo
sur la 1ère ligne seulement, si l'on y trouve.Si oui,
t
branches à la fin du script (et ignore les autres commandes sur la ligne). (Let
fonction des branches d'un label si la plus récentes
appel effectué une réelle substitution; en l'absence d'un label, comme c'est le cas ici, la fin du script est ramifiée à l').Lorsque cela se produit, la gamme d'adresses
1,//
, qui, normalement, trouve la première occurrence à partir de la ligne 2, sera pas match, et la plage de pas être traités, parce que l'adresse est évaluée lorsque la ligne en cours est déjà2
.À l'inverse, si il n'y a pas de match sur la 1ère ligne,
1,//
sera être entré, et trouver le vrai premier match.L'effet net est le même qu'avec GNU
sed
's0,/re/
: seule la première occurrence est remplacé, si elle se produit sur la 1ère ligne ou de toute autre.NON-gamme d'approches
potong réponse montre boucle techniques que de contourner la nécessité d'un; car il utilise GNU
sed
syntaxe, voici les conforme à POSIX équivalents de:Boucle technique 1: Sur le premier match, effectuer la substitution, puis entrer dans une boucle qui imprime simplement les lignes restantes est-à -:
Boucle technique 2, pour de petits fichiers seulement: lire l'ensemble de l'entrée dans la mémoire, puis de réaliser une simple substitution sur elle.
[1] 1.61803 fournit des exemples de ce qui se passe avec
1,/re/
, avec et sanss//
:-
sed '1,/foo/s/foo/bar/' <<<$'1foo\n2foo'
rendements$'1bar\n2bar'
; c'est à dire, les deux lignes ont été mises à jour, parce que le numéro de ligne1
correspond à la 1ère ligne, et regex/foo/
- la fin de la plage - est alors seulement regardé pour le démarrage sur le prochaine ligne. Par conséquent, les deux lignes sont sélectionnées dans ce cas, et les/foo/bar/
de substitution est effectuée sur les deux.-
sed '1,/foo/s//bar/' <<<$'1foo\n2foo\n3foo'
échoue: avecsed: first RE may not be empty
(BSD/macOS) etsed: -e expression #1, char 0: no previous regular expression
(GNU), parce que, au moment de la 1ère ligne est en cours de traitement (en raison de numéro de ligne1
de départ de la gamme), pas de regex a encore été appliqué, de sorte//
ne fait pas référence à quoi que ce soit.À l'exception de GNU
sed
spécial0,/re/
syntaxe, tout gamme qui commence avec une numéro de ligne efficacement s'oppose à l'utilisation de//
.Vous pouvez utiliser awk pour faire quelque chose de similaire..
Explication:
Exécute la déclaration d'action entre {} lorsque la ligne correspond à "#include" et nous n'avons pas déjà traitée.
Cette affiche #include "newfile.h", nous avons besoin d'échapper les guillemets. Ensuite, nous avons défini la variable à 1, afin de ne pas ajouter plus de comprend.
Cela signifie "imprimer la ligne" - action est vide par défaut print $0, qui imprime l'ensemble de la ligne. En une seule ligne et plus facile à comprendre que la sed de l'OMI 🙂
awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json
awk '/#include/ && !done { gsub(/#include/, "include \"newfile.h\""); done=1}; 1' file.c
Tout à fait une collection complète de réponses sur la linuxtopia sed FAQ. Il souligne aussi que certaines réponses fournies ne fonctionne pas avec les non-GNU version de sed, par exemple,
non-GNU version devra être
Cependant, cette version ne fonctionne pas avec gnu sed.
Voici une version qui fonctionne avec:
ex:
Il suffit d'ajouter le numéro de l'occurrence à la fin:
sed
spécifie la commande de substitution avec:[2addr]s/BRE/replacement/flags
et note que "La valeur de flags doit être zéro ou plus de: n Substitut pour la nième occurrence uniquement de la BRE trouvé dans le modèle de l'espace". Ainsi, au moins dans POSIX 2008, la fuite1
n'est pas un GNUsed
extension. En effet, même dans le SUS/POSIX 1997 la norme, cela a été pris en charge, j'ai donc été mal hors de la ligne de retour en 2008.Comment ce script fonctionne: Pour les lignes entre le 1 et le premier
#include
(après la ligne 1), si la ligne commence par#include
, puis ajouter la ligne spécifiée.Cependant, si la première
#include
est dans la ligne 1, puis la ligne 1 et la suivante#include
aura la ligne précédée. Si vous utilisez GNUsed
, il a une extension où0,/^#include/
(au lieu de1,
) va faire la bonne chose.Une solution possible:
Explication:
Je sais que c'est un vieux post mais j'ai eu une solution que j'ai utilisé:
Utiliser grep pour trouver la première occurrence et de s'arrêter là. Également imprimer le numéro de ligne ie 5:ligne. Tuyau en sed et retirez le : et de rien, après si vous êtes juste à gauche avec un numéro de ligne. Tuyau en sed qui ajoute s/.*/remplacer à la fin qui donne la un 1 ligne de script qui est transmis dans le dernier sed pour exécuter un script sur le fichier.
donc si regex = #include et replace = bla et la première occurence grep trouve sur la ligne 5, puis les données joué à la dernière sed serait 5s/.*/bla/.
Si quelqu'un est venu ici pour remplacer un caractère de la première occurrence dans toutes les lignes (comme moi), utilisez ceci:
En changeant de 1 à 2 par exemple, vous pouvez remplacer tous les la deuxième un seul lieu.
's/a/b/'
signifiematch a
, etdo just first match
for every matching line
je voudrais le faire avec un script awk:
puis l'exécuter avec awk:
peut être bâclée, je suis nouveau sur ce.
Comme une alternative suggestion, vous pouvez regarder la
ed
commande.J'ai finalement obtenu ce travail dans un script Bash est utilisé pour insérer un unique, d'horodatage, de chacun des éléments dans un flux RSS:
Il remplace la première occurrence uniquement.
${nowms}
est le temps en millisecondes définie par un script Perl,$counter
est un compteur utilisé pour le contrôle en boucle dans le script,\
permet la commande doit être continue sur la ligne suivante.Le fichier est lu et la sortie standard est redirigée vers un fichier de travail.
La façon dont je le comprends,
1,/====RSSpermalink====/
indique à sed quand arrêter par la définition d'une plage de limitation, puiss/====RSSpermalink====/${nowms}/
est le familier de commande sed pour remplacer la première chaîne avec le deuxième.Dans mon cas, j'ai mis la commande dans des guillemets doubles becauase je suis de l'utiliser dans un script Bash avec des variables.
À l'aide de FreeBSD
ed
et évitered
s '"aucun match" erreur dans le cas ou il n'y a pas deinclude
déclaration dans un fichier à traiter:Cela pourrait fonctionner pour vous (GNU sed):
ou si la mémoire n'est pas un problème:
Avec GNU sed
-z
option que vous pourriez traiter l'intégralité du fichier comme si c'était une seule ligne. De cette façon, uns/…/…/
serait seulement de remplacer le premier match dans l'ensemble du fichier. Rappelez-vous:s/…/…/
remplace seulement le premier match de chaque ligne, mais avec la-z
optionsed
traite le fichier en entier sur une seule ligne.Dans le cas général, vous avez pour reprendre ton expression sed depuis le modèle de l'espace détient désormais la totalité du fichier au lieu d'une seule ligne. Quelques exemples:
s/text.*//
peut être réécrit commes/text[^\n]*//
.[^\n]
correspond tout sauf le caractère de saut de ligne.[^\n]*
correspondent à tous les symboles aprèstext
jusqu'à ce qu'un saut de ligne est atteint.s/^text//
peut être réécrit commes/(^|\n)text//
.s/text$//
peut être réécrit commes/text(\n|$)//
.La commande suivante supprime la première occurrence d'une chaîne dans un fichier. Il supprime la ligne vide aussi. Il est présenté sur un fichier xml, mais il serait de travailler 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 "isTag" tag.
Commande:
Fichier Source (source.txt)
Fichier de résultat (output.txt)
ps: ça n'a pas fonctionné pour moi sur Solaris, SunOS 5.10 (très vieux), mais il fonctionne sur Linux 2.6, sed version 4.1.5
sed
(donc ça n'a pas de travail avec Solaris). Vous devez supprimer cette, s'il vous plaît — il vraiment ne fournit pas distinctif de nouvelles informations à une question qui a déjà été 4½ ans lorsque vous avez répondu. Accordé, il a travaillé par exemple, mais c'est de valeur discutable lorsque la question a autant de réponses que de ce l'on fait.Rien de nouveau, mais peut-être un peu plus concret:
sed -rn '0,/foo(bar).*/s%%\1%p'
Exemple:
xwininfo -name unity-launcher
produit une sortie comme:L'extraction de la fenêtre d'identification avec
xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/s%%\1%p'
produit:POSIXly (également valable dans les sed), Seulement un regex utilisés, ont besoin de mémoire pour une seule ligne (comme d'habitude):
Expliqué: