macos sed gnu inplace-editing

macos - Necesito mi comando sed-i para la edición in situ para que funcione tanto con sed de GNU como con BSD/OSX sed



inplace-editing (7)

Tengo un makefile (desarrollado para gmake en Linux) que estoy tratando de portar a OSX, pero parece que sed no quiere cooperar. Lo que hago es usar GCC para autogenerar archivos de dependencia, y luego ajustarlos un poco usando sed. La parte relevante del archivo MAKE:

$(OBJ_DIR)/%.d: $(SRC_DIR)/%.cpp $(CPPC) -MM -MD $< -o $@ sed -i ''s|/(.*/)/.o:|$(OBJ_DIR)//1.o $(OBJ_DIR)//1.d $(TEST_OBJ_DIR)//1_utest.o:|'' $@

Aunque esto funciona sin problemas bajo GNU / Linux, recibo errores como los siguientes cuando intento construir en OSX:

sed: 1: "test/obj/equipmentConta ...": undefined label ''est/obj/equipmentContainer_utest.d'' sed: 1: "test/obj/dice_utest.d": undefined label ''est/obj/dice_utest.d'' sed: 1: "test/obj/color-string_u ...": undefined label ''est/obj/color-string_utest.d''

Parece que sed está cortando un personaje, pero no veo la solución.


Actally ... haciendo

sed -i -e "s/blah/blah/" files

tampoco hace lo que espera en OS X ... sino que crea archivos de respaldo con la extensión "-e".

El adecuado para OS es

sed -i "" -e "s/blah/blah/" files


Esta no es toda una respuesta a la pregunta, pero uno puede obtener un comportamiento equivalente a Linux a través de brew install gnu-sed --with-default-names


He corregido la solución publicada por @thecarpy:

Aquí hay una solución adecuada de plataforma cruzada para sed -i :

sedi() { case $(uname) in Darwin*) sedi=(''-i'' '''') ;; *) sedi=''-i'' ;; esac LC_ALL=C sed "${sedi[@]}" "$@" }


La respuesta actualmente aceptada es defectuosa de dos maneras muy importantes.

  1. Con BSD sed (la versión OSX), la opción -e se interpreta como una extensión de archivo y, por lo tanto, crea un archivo de copia de seguridad con una extensión -e .

  2. Las pruebas para kernel darwin como se sugiere no son un enfoque confiable para una solución multiplataforma ya que GNU o BSD sed podrían estar presentes en cualquier cantidad de sistemas.

Una prueba mucho más confiable sería simplemente probar la opción de --version que solo se encuentra en la versión GNU de sed.

sed --version >/dev/null 2>&1

Una vez que se determina la versión correcta de sed, podemos ejecutar el comando en su sintaxis adecuada.

Sintaxis de GNU sed para la opción -i:

sed -i -- "$@"

Sintaxis BSD sed para la opción -i:

sed -i "" "$@"

Finalmente, colóquelo todo junto en una función de plataforma cruzada para ejecutar un in situ edit sed commend:

sedi () { sed --version >/dev/null 2>&1 && sed -i -- "$@" || sed -i "" "$@" }

Ejemplo de uso:

sedi ''s/old/new/g'' ''some_file.txt''

Esta solución ha sido probada en OSX, Ubuntu, Freebsd, Cygwin, CentOS, Red Hat Enterprise y Msys.


Me encontré con este problema también y pensé en la siguiente solución:

darwin=false; case "`uname`" in Darwin*) darwin=true ;; esac if $darwin; then sedi="/usr/bin/sed -i ''''" else sedi="sed -i" fi $sedi ''s/foo/bar/'' /home/foobar/bar

funciona para mí ;-), YMMV

Trabajo en un equipo multi-OS donde ppl construyó Windows, Linux y OS X. Algunos usuarios de OS X se quejaron porque obtuvieron otro error: tenían el puerto GNU de sed instalado así que tuve que especificar la ruta completa.


OS X sed maneja el argumento -i diferente a la versión de Linux .

Puede generar un comando que pueda "funcionar" para ambos agregando -e de esta manera:

# vv sed -i -e ''s|/(.*/)/.o:|$(OBJ_DIR)//1.o $(OBJ_DIR)//1.d $(TEST_OBJ_DIR)//1_utest.o:|'' $@

OS X sed -i interpreta lo siguiente después de -i como una extensión de archivo para una copia de seguridad de la edición in situ. (La versión de Linux solo hace esto si no hay espacio entre -i y la extensión.) Obviamente, un efecto secundario de usar esto es que obtendrá un archivo de copia de seguridad con -e como una extensión, que puede que no desee. Consulte otras respuestas a esta pregunta para obtener más detalles y enfoques más limpios que se pueden usar en su lugar.

El comportamiento que ves es porque OS X sed consume el s||| como la extensión (!) interpreta el siguiente argumento como un comando, en este caso comienza con t , que sed reconoce como un comando de rama a etiqueta esperando la etiqueta de destino como un argumento, de ahí el error que ve.

Si crea una test archivo, puede reproducir el error:

$ sed -i ''s|x|y|'' test sed: 1: "test": undefined label ''est''


La útil respuesta de Martin Clayton proporciona una buena explicación del problema [1] , pero una solución que, como afirma, tiene un efecto secundario potencialmente no deseado.

Aquí hay soluciones libres de efectos secundarios :

Advertencia : Resolver el problema de sintaxis -i solo, como se muestra a continuación, puede no ser suficiente, porque hay muchas otras diferencias entre GNU sed y BSD / macOS sed (para una discusión exhaustiva, vea esta respuesta mía).

Solución con -i : crea un archivo de copia de seguridad temporalmente y luego límpialo:

Con un argumento opcional de sufijo no vacío (extensión de archivo de nombre de archivo de reserva) (un valor que no es la cadena vacía ), puede usar -i de una manera que funcione tanto con BSD / macOS sed como con GNU sed , añadiendo directamente el sufijo a la opción -i .

Esto se puede utilizar para crear un archivo de copia de seguridad de forma temporal que puede limpiar de inmediato:

sed -i.bak ''s/foo/bar/'' file && rm file.bak

Obviamente, si desea mantener la copia de seguridad, simplemente omita la parte && rm file.bak .

Solución alternativa que es POSIX-compatible, usando un archivo temporal y mv :

Si solo se va a editar un solo archivo in situ, se puede eludir la opción -i para evitar la incompatibilidad.

Si restringe su script sed y otras opciones a las características que cumplen con POSIX , la siguiente es una solución totalmente portátil (tenga en cuenta que -i no es compatible con POSIX ).

sed ''s/foo/bar'' file > /tmp/file.$$ && mv /tmp/file.$$ file

  • Este comando simplemente escribe las modificaciones en un archivo temporal y, si el comando sed tiene éxito ( && ), reemplaza el archivo original por el temporal.

    • Si desea conservar el archivo original como copia de seguridad, agregue otro comando mv que renombre primero el original.
  • Advertencia : Fundamentalmente, esto es lo que -i también, excepto que trata de conservar los permisos y atributos extendidos (macOS) del archivo original; sin embargo, si el archivo original es un enlace simbólico , tanto esta solución como -i reemplazarán el enlace simbólico con un archivo común.
    Consulte la mitad inferior de esta respuesta mía para obtener detalles sobre cómo funciona -i .

[1] Para una explicación más detallada, mira esta respuesta mía.