Rechercher et remplacer dans le fichier et écraser le fichier ne fonctionne pas, il vide le fichier

604

Je voudrais exécuter une recherche et un remplacement sur un fichier HTML via la ligne de commande.

Ma commande ressemble à ceci:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html

Lorsque je lance cela et regarde le fichier par la suite, il est vide. Il a supprimé le contenu de mon fichier.

Lorsque j'exécute ceci après avoir à nouveau restauré le fichier:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

C'est stdoutle contenu du fichier et la recherche et le remplacement ont été exécutés.

Pourquoi cela arrive-t-il?

BBales
la source
13
Alternative à Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
sedcommande beaucoup plus connexe pour trouver une chaîne et remplacer toute la ligne: stackoverflow.com/questions/11245144/…
cregox

Réponses:

917

Lorsque le shell voit > index.htmldans la ligne de commande, il ouvre le fichier index.htmlpour l' écriture , effaçant tout son contenu précédent.

Pour résoudre ce problème, vous devez passer l' -ioption pour sedeffectuer les modifications en ligne et créer une sauvegarde du fichier d'origine avant d'effectuer les modifications sur place:

sed -i.bak s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

Sans le .bak, la commande échouera sur certaines plates-formes, telles que Mac OSX.

codaddict
la source
20
Dire truncates the fileau lieu de le opens the filerend probablement plus clair.
Mikel
12
Au moins sur mon mac, la première suggestion ne fonctionne pas ... si vous effectuez un remplacement sur place sur un fichier, vous devez spécifier une extension. Vous pouvez, au moins, transmettre une extension de longueur nulle cependant: sed -i 's / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza
5
pour les variables sed -i.bak's / '$ search' / '$ replace' / g 'index.html
Fatima Zohra
33
sur osx, utilisez une chaîne vide '' comme paramètre pour -i, comme:sed -i '' 's/blah/xx/g'
Pierre Houston
4
mais quelle est la vôtre .bakaprès sed -i?
Patrizio Bertoni
210

Un modèle alternatif utile est:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

Cela a à peu près le même effet, sans utiliser l' -ioption, et signifie en outre que, si le script sed échoue pour une raison quelconque, le fichier d'entrée n'est pas encombré. De plus, si la modification réussit, il ne reste aucun fichier de sauvegarde. Ce type d'idiome peut être utile dans les Makefiles.

Beaucoup de graines ont l' -ioption, mais pas toutes; le posix sed est celui qui ne fonctionne pas. Si vous visez la portabilité, il est donc préférable de l'éviter.

Norman Gray
la source
9
+1 pour aucun fichier de sauvegarde qui traîne et qui n'encombre pas le fichier d'entrée si la modification échoue. Fonctionne parfaitement sur mac.
Mike Grace
A parfaitement fonctionné pour moi. Je vous remercie! (sur un Mac)
intéressé le
1
Cela a fonctionné parfaitement pour moi où sur Ubuntu Server 14.04 sed -i a continué de mettre à zéro le fichier.
Chris Giddings
2
Amélioration extrêmement mineure:... && mv index.html{.tmp,}
EdwardGarson
5
@EdwardGarson En effet, c'est probablement ce que j'utiliserais si je le tapais - je suis d'accord qu'il est plus net - mais sh(si je me souviens bien) n'a pas cette {...}extension. Dans un Makefile, vous utilisez peut-être shplutôt que bash, donc si vous visez la portabilité (ou la posixité), vous devrez éviter cette construction.
Norman Gray
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Cela effectue une substitution globale sur place dans le fichier index.html. La citation de la chaîne évite les problèmes d'espaces dans la requête et le remplacement.

Rich Apodaca
la source
57

utilisez l'option -i de sed, par exemple

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
la source
Qu'est-ce que ça veut dire? sed: -i ne peut pas être utilisé avec stdin
sheetal
2
N'oubliez pas d'entourer votre motif entre guillemets s'il contient des espaces -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson
@sheetal: -ieffectue l'édition sur place des fichiers , il n'est donc pas logique de le combiner avec l' entrée stdin .
mklement0
Cela peut fonctionner sur macOS, mais pas sur Arch Linux pour moi.
xdevs23
Sans le -e, la réponse acceptée ne fonctionne pas sur MacOS, Catalina. Avec le -e, cela fonctionne.
cwhiii
18

Pour modifier plusieurs fichiers (et enregistrer une sauvegarde de chacun sous * .bak):

perl -p -i -e "s/\|/x/g" *  

prendra tous les fichiers dans le répertoire et les remplacera |par x ceci est appelé un "gâteau Perl" (facile comme un gâteau)

Stenemo
la source
1
C'est bien de voir quelqu'un prêt à regarder l'énoncé du problème, et pas seulement les balises. OP n'a pas spécifié sedcomme exigence, il ne l'a utilisé que comme outil déjà essayé.
user7412956
14

Vous devriez essayer d'utiliser l'option -ide modification sur place.

uloBasEI
la source
6
sed -i.bak "s#https.*\.com#$pub_url#g" MyHTMLFile.html

Si vous avez un lien à ajouter, essayez ceci. Recherchez l'URL comme ci-dessus (en commençant par https et en terminant par.com ici) et remplacez-la par une chaîne d'URL. J'ai utilisé une variable $pub_urlici. ssignifie ici recherche etg remplacement global.

Ça marche !

Kaey
la source
6

Attention: c'est une méthode dangereuse! Il abuse des tampons d'E / S sous Linux et avec des options spécifiques de mise en mémoire tampon, il parvient à travailler sur de petits fichiers. C'est une curiosité intéressante.Mais ne l'utilisez pas pour une situation réelle!

Outre l' -ioption de sed vous pouvez utiliser l' teeutilitaire .

De man:

tee - lire à partir de l'entrée standard et écrire sur la sortie standard et les fichiers

Ainsi, la solution serait:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

- ici le teeest répété pour s'assurer que le pipeline est tamponné. Ensuite, toutes les commandes du pipeline sont bloquées jusqu'à ce qu'elles obtiennent une entrée sur laquelle travailler. Chaque commande du pipeline démarre lorsque les commandes en amont ont écrit 1 tampon d'octets (la taille est définie quelque part ) à l'entrée de la commande. Donc, la dernière commandetee index.html , qui ouvre le fichier pour l'écriture et le vide donc, s'exécute après la fin du pipeline en amont et la sortie est dans le tampon dans le pipeline.

Les éléments suivants ne fonctionneront probablement pas:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

- il exécutera les deux commandes du pipeline en même temps sans aucun blocage. (Sans bloquer, le pipeline doit passer les octets ligne par ligne au lieu de tampon par tampon. Comme lors de l'exécution cat | sed s/bar/GGG/. Sans blocage, il est plus interactif et généralement les pipelines de seulement 2 commandes s'exécutent sans mise en mémoire tampon et blocage. Les pipelines plus longs sont mis en mémoire tampon.) La tee index.htmlvolonté ouvrez le fichier pour l'écriture et il sera vidé. Cependant, si vous activez toujours la mise en mémoire tampon, la deuxième version fonctionnera également.

xealits
la source
3
Le fichier de sortie de tee est également ouvert immédiatement, ce qui entraîne un index.html vide pour la commande entière.
sjngm
3
Cela corrompra tout fichier d'entrée plus volumineux que le tampon du pipeline (qui est généralement de 64 Ko) . (@sjngm: le fichier n'est pas tronqué instantanément comme avec >, mais le fait est que c'est une solution cassée qui est susceptible d'entraîner une perte de données).
mklement0
4

Le problème avec la commande

sed 'code' file > file

est-ce file le shell est tronqué avant que sed ne puisse réellement le traiter. En conséquence, vous obtenez un fichier vide.

La méthode sed pour ce faire est d'utiliser -ipour éditer sur place, comme d'autres réponses l'ont suggéré. Cependant, ce n'est pas toujours ce que vous voulez. -icréera un fichier temporaire qui sera ensuite utilisé pour remplacer le fichier d'origine. Cela pose problème si votre fichier d'origine était un lien (le lien sera remplacé par un fichier standard). Si vous devez conserver des liens, vous pouvez utiliser une variable temporaire pour stocker la sortie de sed avant de la réécrire dans le fichier, comme ceci:

tmp=$(sed 'code' file); echo -n "$tmp" > file

Mieux encore, utilisez printfplutôt que echopuisque echoest susceptible de traiter \\comme \dans certains shells (par exemple, tiret):

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Andrzej Pronobis
la source
1
+1 pour conserver les liens. Il fonctionne également avec un fichier temporaire:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha
3

Et la edréponse:

printf "%s\n" '1,$s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' w q | ed index.html

Pour réitérer ce codaddict répondu , les poignées de shell la redirection d' abord , effaçant le fichier « input.html », et puis le shell appelle la commande « sed » passer un fichier maintenant vide.

glenn jackman
la source
2
question rapide, pourquoi les gens continuent de donner "la edversion" des sedréponses? fonctionne-t-il plus rapidement?
cregox
6
Certains sedne mettent pas en œuvre la -imodification sur place. edest omniprésent et vous permet d'enregistrer vos modifications dans le fichier d'origine. De plus, il est toujours bon d'avoir beaucoup d'outils dans votre kit.
glenn jackman
OK cool. donc, en termes de performances, ce sont les mêmes, je suppose. Merci!
cregox
2

Vous pouvez utiliser Vim en mode Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % sélectionner toutes les lignes

  2. x sauver et fermer

Steven Penny
la source
0

Je cherchais l'option où je peux définir la plage de lignes et j'ai trouvé la réponse. Par exemple, je veux changer host1 en host2 de la ligne 36-57.

sed '36,57 s/host1/host2/g' myfile.txt > myfile1.txt

Vous pouvez également utiliser l'option gi pour ignorer la casse des caractères.

sed '30,40 s/version/story/gi' myfile.txt > myfile1.txt

la source
0

Avec tout le respect dû aux réponses correctes ci-dessus, c'est toujours une bonne idée de faire un "dry run" scripts comme ça, afin de ne pas corrompre votre fichier et de devoir recommencer à zéro.

Obtenez simplement votre script pour renverser la sortie sur la ligne de commande au lieu de l'écrire dans le fichier, par exemple, comme ça:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

OU

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

De cette façon, vous pouvez voir et vérifier la sortie de la commande sans avoir à tronquer votre fichier.

Nestor Milyaev
la source