Buscar y reemplazar en el archivo y sobrescribir el archivo no funciona, vacía el archivo

604

Me gustaría ejecutar una búsqueda y reemplazo en un archivo HTML a través de la línea de comando.

Mi comando se parece a esto:

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

Cuando ejecuto esto y miro el archivo después, está vacío. Se eliminó el contenido de mi archivo.

Cuando ejecuto esto después de restaurar el archivo nuevamente:

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

El stdoutes el contenido del archivo, y la búsqueda y reemplazo se ha ejecutado.

¿Por qué está pasando esto?

BBales
fuente
13
Alternativa de Perl:perl -pi -w -e 's/STRING_TO_REPLACE/REPLACE_WITH/g;' index.html
Gjorgji Tashkovski
sedcomando muy relacionado para encontrar una cadena y reemplazar toda la línea: stackoverflow.com/questions/11245144/…
cregox

Respuestas:

917

Cuando el shell ve > index.htmlen la línea de comando, abre el archivo index.htmlpara escribir , borrando todo su contenido anterior.

Para solucionar esto, debe pasar la -iopción de sedrealizar los cambios en línea y crear una copia de seguridad del archivo original antes de que los cambios se realicen en el lugar:

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

Sin el .bak, el comando fallará en algunas plataformas, como Mac OSX.

codictorio
fuente
20
Decir en truncates the filelugar de opens the fileprobablemente lo aclara.
Mikel
12
Al menos en mi Mac, la primera sugerencia no funciona ... si está haciendo un reemplazo in situ en un archivo, debe especificar una extensión. Sin embargo, puede pasar una extensión de longitud cero: sed -i '' s / STRING_TO_REPLACE / STRING_TO_REPLACE_IT / g index.html
Tom Lianza
55
para las variables sed -i.bak 's /' $ search '/' $ replace '/ g' index.html
Fatima Zohra
33
en osx, use una cadena vacía '' como parámetro para -i, como:sed -i '' 's/blah/xx/g'
Pierre Houston
44
pero ¿cuál es el tuyo .bakdespués sed -i?
Patrizio Bertoni
210

Un patrón alternativo y útil es:

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

Eso tiene el mismo efecto, sin usar la -iopción, y además significa que, si la secuencia de comandos sed falla por alguna razón, el archivo de entrada no está bloqueado. Además, si la edición es exitosa, no queda ningún archivo de respaldo. Este tipo de idioma puede ser útil en Makefiles.

Muchos seds tienen la -iopción, pero no todos; el posix sed es uno que no. Si busca la portabilidad, por lo tanto, es mejor evitarlo.

Gris normando
fuente
99
+1 para que no haya ningún archivo de copia de seguridad y que no bloquee el archivo de entrada si falla la edición Trabajó sin problemas en mac.
Mike Grace
Me funcionó perfectamente. ¡Gracias! (en una Mac)
interesado
1
Esto funcionó perfectamente para mí en donde en Ubuntu Server 14.04 sed -i seguía poniendo a cero el archivo.
Chris Giddings
2
Mejora extremadamente menor:... && mv index.html{.tmp,}
EdwardGarson
55
@EdwardGarson De hecho, eso es probablemente lo que usaría si lo estuviera escribiendo, estoy de acuerdo en que es más ordenado, pero sh(si no recuerdo mal) no tiene esa {...}expansión. En un Makefile que podría estar usando en shlugar de hacerlo bash, por lo que si está buscando la portabilidad (o posixness), deberá evitar esa construcción.
Norman Gray el
95
sed -i 's/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g' index.html

Esto hace una sustitución global en el lugar en el archivo index.html. Citar la cadena evita problemas con espacios en blanco en la consulta y el reemplazo.

Rich Apodaca
fuente
57

use la opción -i de sed, p. ej.

sed -i bak -e s/STRING_TO_REPLACE/REPLACE_WITH/g index.html
Kevin
fuente
¿Qué significa esto? sed: -i no puede usarse con stdin
sheetal
2
Recuerde rodear su patrón entre comillas si contiene espacios en blanco -'s/STRING_TO_REPLACE/REPLACE_WITH/g'
Doug Thompson
@sheetal: -irealiza la edición de archivos en el lugar , por lo que no tiene sentido combinarlo con la entrada estándar .
mklement0
Esto podría funcionar en macOS, pero no en Arch Linux para mí.
xdevs23
Sin el -e, la respuesta aceptada no funciona en MacOS, Catalina. Con el -e funciona.
cwhiii
18

Para cambiar varios archivos (y guardar una copia de seguridad de cada uno como * .bak):

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

tomará todos los archivos en el directorio y los reemplazará |con x esto se llama un "pastel Perl" (fácil como un pastel)

Stenemo
fuente
1
Es bueno ver a alguien dispuesto a mirar el enunciado del problema, y ​​no solo las etiquetas. OP no especificó sedcomo requisito, solo lo usó como la herramienta que ya probó.
user7412956
14

Debería intentar usar la opción -ipara la edición in situ.

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

Si tiene que agregar un enlace, intente esto. Busque la URL como se indica arriba (comenzando con https y terminando con.com aquí) y reemplácela con una cadena de URL. He usado una variable $pub_urlaquí. saquí significa buscar yg significa reemplazo global.

Funciona !

Kaey
fuente
6

Advertencia: este es un método peligroso! Abusa de los búferes de E / S en Linux y con opciones específicas de almacenamiento en búfer se las arregla para trabajar en archivos pequeños. Es una curiosidad interesante. ¡Pero no lo use para una situación real!

Además de la -iopción de sed usted puede usar la teeutilidad .

De man:

tee: lea desde la entrada estándar y escriba en la salida y archivos estándar

Entonces, la solución sería:

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

- aquí teese repite para asegurarse de que la tubería esté almacenada. Luego, todos los comandos en la tubería se bloquean hasta que obtienen alguna entrada para trabajar. Cada comando en la tubería comienza cuando los comandos aguas arriba han escrito 1 búfer de bytes (el tamaño se define en alguna parte ) en la entrada del comando. Entonces el último comandotee index.html , que abre el archivo para escribir y, por lo tanto, lo vacía, se ejecuta después de que la tubería ascendente ha finalizado y la salida está en el búfer dentro de la tubería.

Lo más probable es que lo siguiente no funcione:

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

- ejecutará ambos comandos de la tubería al mismo tiempo sin ningún bloqueo. (Sin el bloqueo de la tubería debe pasar la línea de bytes por línea en lugar de tampón de memoria intermedia. Igual que cuando se ejecuta cat | sed s/bar/GGG/. Sin bloqueo es más interactiva y por lo general las tuberías de sólo 2 comandos ejecutar sin memoria intermedia y el bloqueo. Oleoductos más largos se almacenan temporalmente.) La tee index.htmlvoluntad abra el archivo para escribir y se vaciará. Sin embargo, si activa el almacenamiento en búfer siempre, la segunda versión también funcionará.

xealits
fuente
3
El archivo de salida de tee también se abre inmediatamente, lo que da como resultado un index.html vacío para todo el comando.
sjngm
3
Esto dañará cualquier archivo de entrada que sea más grande que el búfer de canalización (que generalmente es de 64 KB) . (@sjngm: el archivo no se trunca instantáneamente como con >, pero el punto es que es una solución dañada que probablemente provocará la pérdida de datos).
mklement0
4

El problema con el comando

sed 'code' file > file

es que fileel shell trunca antes de que sed realmente lo procese. Como resultado, obtienes un archivo vacío.

La forma de hacerlo es usar la -iedición en el lugar, como sugieren otras respuestas. Sin embargo, esto no siempre es lo que quieres. -icreará un archivo temporal que luego se usará para reemplazar el archivo original. Esto es problemático si su archivo original era un enlace (el enlace será reemplazado por un archivo normal). Si necesita preservar enlaces, puede usar una variable temporal para almacenar la salida de sed antes de volver a escribirla en el archivo, de esta manera:

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

Mejor aún, es probable que el uso en printflugar de echodesde ya echose procese \\como \en algunos shells (por ejemplo, guión)

tmp=$(sed 'code' file); printf "%s" "$tmp" > file
Andrzej Pronobis
fuente
1
+1 para preservar enlaces. También funciona con un archivo temporal:sed 'code' file > file.tmp; cat file.tmp > file; rm file.tmp
dashohoxha
3

Y la edrespuesta:

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

Para reiterar lo que respondió codaddict , el shell maneja primero la redirección , borrando el archivo "input.html", y luego el shell invoca el comando "sed" pasándole un archivo ahora vacío.

Glenn Jackman
fuente
2
pregunta rápida, ¿por qué la gente sigue dando "la edversión" de las sedrespuestas? ¿funciona más rápido?
cregox
66
Algunos seds no se implementan -ipara editar en el lugar. edes omnipresente y le permite guardar sus ediciones en el archivo original. Además, siempre es bueno tener muchas herramientas en su kit.
Glenn Jackman
está bien. así que, en cuanto al rendimiento, supongo que son lo mismo. ¡Gracias!
cregox
2

Puede usar Vim en modo Ex:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % seleccione todas las líneas

  2. x guardar y cerrar

Steven Penny
fuente
0

Estaba buscando la opción donde puedo definir el rango de línea y encontré la respuesta. Por ejemplo, quiero cambiar host1 a host2 de la línea 36-57.

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

También puede usar la opción gi para ignorar el caso de caracteres.

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

fuente
0

Con el debido respeto a las respuestas correctas anteriores, siempre es una buena idea "ejecutar en seco" secuencias de comandos como esa, para que no corrompa su archivo y tenga que comenzar de nuevo desde cero.

Simplemente haga que su secuencia de comandos derrame la salida a la línea de comando en lugar de escribirla en el archivo, por ejemplo, así:

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

O

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

De esta manera, puede ver y verificar la salida del comando sin truncar su archivo.

Néstor Milyaev
fuente