El comando sed con la opción -i falla en Mac, pero funciona en Linux


303

He utilizado con éxito el siguiente sedcomando para buscar / reemplazar texto en Linux:

sed -i 's/old_link/new_link/g' *

Sin embargo, cuando lo pruebo en mi Mac OS X, obtengo:

"el comando c espera \ seguido de texto"

Pensé que mi Mac ejecuta un shell BASH normal. ¿Qué pasa?

EDITAR:

De acuerdo con @High Performance, esto se debe a que Mac tiene sedun sabor diferente (BSD), por lo que mi pregunta sería, ¿cómo puedo replicar este comando en BSD sed?

EDITAR:

Aquí hay un ejemplo real que causa esto:

sed -i 's/hello/gbye/g' *

1
Esto significa que sedve una "c" en sus datos como un comando. ¿Estás usando una variable? Publique algo que represente más de cerca el comando real y algunos datos que está procesando. Puede obtener una demostración simple de este error haciendo echo x | sed c.
Pausado hasta nuevo aviso.

@Dennis, el simple comando anterior causa esto, aunque los datos que está procesando es un sitio web completo (estoy convirtiendo todos los enlaces de imágenes), incluidos los archivos html y css ...
Yarin

Respuestas:


397

Si usa la -iopción, debe proporcionar una extensión para sus copias de seguridad.

Si usted tiene:

File1.txt
File2.cfg

El comando (tenga en cuenta la falta de espacio entre -iy ''y el -ede hacer que funcione en las nuevas versiones de Mac y en GNU):

sed -i'.original' -e 's/old_link/new_link/g' *

Crea 2 archivos de respaldo como:

File1.txt.original
File2.cfg.original

No hay una forma portátil de evitar hacer archivos de copia de seguridad porque es imposible encontrar una combinación de comandos sed que funcione en todos los casos:

  • sed -i -e ...- no funciona en OS X ya que crea -ecopias de seguridad
  • sed -i'' -e ... - no funciona en OS X 10.6 pero funciona en 10.9+
  • sed -i '' -e ... - no funciona en GNU

Nota Dado que no hay un comando sed funcionando en todas las plataformas, puede intentar usar otro comando para lograr el mismo resultado.

P.ej, perl -i -pe's/old_link/new_link/g' *


44
Tuve el mismo problema. Gracias por esta solución Pero donde intenté con 'man sed' para encontrar la descripción de '-i', no hay nada sobre usar -i '' para ignorar las copias de seguridad. Esta es mi primera culpa. En segundo lugar, cuando aparece el error "comando espera \ seguido de texto", ¿por qué no nos dice directamente que espera un nombre de respaldo para la opción '-i'! Tal cosa sucede en todas partes: obtienes un error pero no por qué el error, luego buscas el manual que no explica nada al respecto. Luego lo buscas en Google para encontrar a alguien más que también tiene el mismo problema. Quiero decir, ¿por qué no dar ejemplo en el manual?
lukmac

o díganos por qué existe el error en lugar de solo un mensaje sin información de que ocurre un error Esta es una sugerencia para todos los fabricantes de herramientas, si alguna vez pueden leer este comentario.
lukmac

55
@lukmac Por lo que sedpuedo decir, usted proporcionó un sufijo de respaldo. El sufijo de respaldo es s/old_link/new_link/g. Se supone que el siguiente argumento después de eso son los comandos de edición. Debido a que interpretó los comandos como el nombre de la copia de seguridad, tomó el primer nombre de archivo como los comandos de edición, pero no fueron válidos.
Barmar

2
¿Será esto siempre un problema? ¿Hay alguna manera de que Apple pueda crear una solución / paquete que GNU sed con OSX? O bien, ¿GNU no pudo soportar sed -i '' -e ...?
blong

55
sed -i'' -eparece no funcionar como se esperaba en mac 10.14
MoOx

64

Creo en OS X cuando usas -i se requiere una extensión para los archivos de respaldo . Tratar:

sed -i .bak 's/hello/gbye/g' *

Usando GNU sedla extensión es opcional .


55

Esto funciona con las versiones GNU y BSD de sed:

sed -i'' -e 's/old_link/new_link/g' *

o con respaldo:

sed -i'.bak' -e 's/old_link/new_link/g' *

¡Observe el espacio perdido después de la -iopción! (Necesario para GNU sed)


77
El primero no funciona en OSX (lo acabo de probar en 10.6.8)
marcin

44
Para mí en OS X (10.10.3), el primero creó archivos de copia de seguridad con el sufijo -e. No es bueno. El segundo fue lo único que funcionó para mí consistentemente entre Ubuntu y OS X. Sin embargo, no quería archivos de copia de seguridad, así que tuve que ejecutar un rmcomando justo después para eliminarlo.
Brendan

1
La primera línea debe reescribirse con un espacio para trabajar en 10.10: sed -i'' ...=>sed -i '' ...
Daniel Jomphe

3
@DanielJomphe Pero agregar este espacio no funciona en GNU sed
Brice

1
@marcin primero funciona para mí también en OSX 10.11.2. Lo único es que crea un archivo de copia de seguridad, que debe eliminarse después. El segundo se ve mejor, ya que no tenemos que averiguar el nombre de archivo más adelante.
vikas027

41

Tuve el mismo problema en Mac y lo resolvió con brew:

brew install gnu-sed

y usar como

gsed SED_COMMAND

también puede establecer como sedalias gsed(si lo desea):

alias sed=gsed

3
¿Por qué dio exactamente la misma respuesta que Ohad Kravchick ?
gniourf_gniourf

1
establecer un alias como este no es una gran idea
SantaXL

1
En lugar de un alias, como se recomienda en la página de preparación correspondiente, agréguelo a la ruta: PATH = "$ (brew --prefix) / opt / gnu-sed / libexec / gnubin: $ PATH"
y.luis

24

O bien, puede instalar la versión GNU de sed en su Mac, llamada gsed, y usarla utilizando la sintaxis estándar de Linux.

Para eso, instale gsedusando puertos (si no lo tiene, consígalo en http://www.macports.org/ ) ejecutando sudo port install gsed. Entonces, puedes corrersed -i 's/old_link/new_link/g' *


24
.. o si usas homebrew, entonces instálalognu-sed
Sudar

1
Gracias @Sudar, ¡doble aprobación!
Andrew Swan

9

De hecho, su Mac ejecuta un shell BASH, pero esta es más una cuestión de qué implementación de sed está tratando. En una Mac sed proviene de BSD y es sutilmente diferente de la sed que puede encontrar en una caja típica de Linux. Te sugiero man sed.


3
Gracias por señalar el problema de BSD. Pero soy bastante analfabeta y solo necesito una solución rápida para mi comando. Una rápida mirada al hombre no me dice nada
Yarin

55
@Yarin: no, y si echo un vistazo rápido a man sed, eso tampoco te dice nada. Prueba una mirada más larga.
Alto rendimiento Mark

21
La mayoría de las respuestas también lo son enterrados en algún lugar de un hombre, pero eso es lo que también lo es la gente ocupada de lucro que necesitan respuestas de las personas inteligentes
Yarin

9

La respuesta de Sinetris es correcta, pero uso esto con el findcomando para ser más específico sobre los archivos que quiero cambiar. En general, esto debería funcionar (probado en osx /bin/bash):

find . -name "*.smth" -exec sed -i '' 's/text1/text2/g' {} \;

En general, cuando se usa sedsin findproyectos complejos es menos eficiente.


-exec¡es muy lindo! Solo me pregunto si la barra al final es realmente necesaria
Ye Liu

2
@YeLiu: sin el \, el ;intérprete lo interpretó cuando lo intenté
serv-inc

2

He creado una función para manejar la seddiferencia entre MacOS (probado en MacOS 10.12) y otro sistema operativo:

OS=`uname`
# $(replace_in_file pattern file)
function replace_in_file() {
    if [ "$OS" = 'Darwin' ]; then
        # for MacOS
        sed -i '' -e "$1" "$2"
    else
        # for Linux and Windows
        sed -i'' -e "$1" "$2"
    fi
}

Uso:

$(replace_in_file 's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' "./mysql/.env")

Dónde:

, es un delimitador

's,MASTER_HOST.*,MASTER_HOST='"$MASTER_IP"',' es patrón

"./mysql/.env" es la ruta al archivo


1

Aquí se explica cómo aplicar variables de entorno al archivo de plantilla (sin necesidad de copia de seguridad).

1. Cree una plantilla con {{FOO}} para reemplazarla más tarde.

echo "Hello {{FOO}}" > foo.conf.tmpl

2. Reemplace {{FOO}} con la variable FOO y envíe al nuevo archivo foo.conf

FOO="world" && sed -e "s/{{FOO}}/$FOO/g" foo.conf.tmpl > foo.conf

Trabajando tanto macOS 10.12.4 como Ubuntu 14.04.5


0

Como indican las otras respuestas, no hay una manera de usar sed de manera portátil en OS X y Linux sin hacer archivos de respaldo. Entonces, en su lugar, usé esta línea de Ruby para hacerlo:

ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml

En mi caso, necesitaba llamarlo desde una raketarea (es decir, dentro de un script Ruby), así que utilicé este nivel adicional de citas:

sh %q{ruby -pi -e "sub(/ $/, '')" ./config/locales/*.yml}

-1
sed -ie 's/old_link/new_link/g' *

Funciona tanto en BSD como en Linux


3
Esto crea en Linux otro nombre de archivo con eagregado
Brice

1
Roto, mejor quitarlo.
sorin

Está funcionando en Mac y Linux. ¿Cuál es el problema con esta solución?
Islam Azab

No, no funciona en Mac BSD Sed: crearía un archivo de respaldo con 'e' agregado al nombre del archivo.
Jesper Grann Laursen
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.