Indicador de sed in situ que funciona tanto en Mac (BSD) como en Linux

237

¿Existe una invocación de sededición in situ sin copias de seguridad que funcione tanto en Linux como en Mac? Si bien el BSD sedenviado con OS X parece necesitar sed -i '' …, las seddistribuciones GNU Linux generalmente vienen con interpreta las comillas como un nombre de archivo de entrada vacío (en lugar de la extensión de copia de seguridad), y necesita en su sed -i …lugar.

¿Existe alguna sintaxis de línea de comando que funcione con ambos tipos, por lo que puedo usar el mismo script en ambos sistemas?

dnadlinger
fuente
¿Perl no es una opción? ¿Debes usar sed?
Noufal Ibrahim
¡Tal vez instale la versión GNU y úsela! topbug.net/blog/2013/04/14/… ? Lo intentaría primero. Por otra parte, ese tipo de apesta también.
Dmitry Minkovsky
2
@dimadima: Puede ser interesante para otras personas que exploran esta pregunta y que tienen scripts personales que se rompen en su máquina OS X. Sin embargo, en mi caso, lo necesitaba para el sistema de compilación de un proyecto de código abierto, donde decirle a su usuario que instale GNU sed primero habría derrotado el propósito original de este ejercicio (parchear algunos archivos de manera "funciona en todas partes") .
dnadlinger
@klickverbot, sí, tiene sentido. Primero agregué el comentario como respuesta, y luego lo eliminé, dándome cuenta de que no era una respuesta a tu pregunta :).
Dmitry Minkovsky
1
Referencia cruzada: ¿Cómo lograr la portabilidad con sed -i (edición in situ)? en el intercambio de pila de Unix y Linux.
MvG

Respuestas:

212

Si realmente quiere usar sed -ila forma 'fácil', lo siguiente FUNCIONA tanto en GNU como en BSD / Mac sed:

sed -i.bak 's/foo/bar/' filename

Tenga en cuenta la falta de espacio y el punto.

Prueba:

# GNU sed
% sed --version | head -1
GNU sed version 4.2.1
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

# BSD sed
% sed --version 2>&1 | head -1
sed: illegal option -- -
% echo 'foo' > file
% sed -i.bak 's/foo/bar/' ./file
% ls
file  file.bak
% cat ./file
bar

Obviamente, podría simplemente eliminar los .bakarchivos.

vacas
fuente
2
Este es el camino a seguir. Agregue algunos caracteres y es portátil. Eliminar el archivo de copia de seguridad es trivial (ya tiene el nombre de archivo cuando se invoca sed)
slezica
Esto funcionó en Mac, pero en Ubuntu 16.04 sobrescribe el archivo original a 0 bytes y crea el archivo .bak también con 0 bytes.
house9
1
Es de destacar que en MacOS Sierra (y posiblemente antes, no tengo a mano una maquina), sedno acepta una posición commandargumento cuando se invoca con -i- que debe ser suministrado con otra bandera, -e. Por lo tanto, el comando se convierte en:sed -i.bak -e 's/foo/bar/' filename
ELLIOTTCABLE
55
Esto crea copias de seguridad, mientras que el OP solicita específicamente la edición en el lugar.
Marc-André Lafortune
8
Entonces la solución completa es:sed -i.bak 's/foo/bar/' file && rm file.bak
joeytwiddle
112

Esto funciona con GNU sed, pero no en OS X:

sed -i -e 's/foo/bar/' target.file
sed -i'' -e 's/foo/bar/' target.file

Esto funciona en OS X, pero no con GNU sed:

sed -i '' -e 's/foo/bar/' target.file

En OS X usted

  • no se puede usar sed -i -eya que la extensión del archivo de respaldo se establecería en-e
  • no se puede usar sed -i'' -epor las mismas razones: necesita un espacio entre -iy ''.
dnadlinger
fuente
1
@AlexanderMills Le dice a sed que el siguiente argumento es un comando; también podría omitirse aquí.
Benjamin W.
44
Tenga en cuenta que -iy -i''son idénticos en el tiempo de análisis de shell; sed no puede comportarse de manera diferente en esas dos primeras invocaciones, porque obtiene exactamente los mismos argumentos.
wchargin
44

Cuando estoy en OSX, siempre instalo la versión de GNU sed a través de Homebrew, para evitar problemas en los scripts, porque la mayoría de los scripts fueron escritos para versiones de GNU sed.

brew install gnu-sed --with-default-names

Entonces su BSD sed será reemplazado por GNU sed.

Alternativamente, puede instalar sin nombres predeterminados, pero luego:

  • Cambie su PATHcomo se indica después de instalargnu-sed
  • Verifique sus scripts para elegir entre gsedo seddependiendo de su sistema
Lewy
fuente
44
el --with-default-namesfue retirado de homebrew núcleos , más información en esta respuesta. al instalar gnu-sedahora, las instrucciones de instalación especifican que necesita agregar gnubina su PATH:PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
pgericson
18

Como Noufal Ibrahim pregunta, ¿por qué no puedes usar Perl? Cualquier Mac tendrá Perl, y hay muy pocas distribuciones de Linux o BSD que no incluyan alguna versión de Perl en el sistema base. Uno de los únicos entornos que podrían carecer de Perl sería BusyBox (que funciona como GNU / Linux para-i , excepto que no se puede especificar una extensión de copia de seguridad).

Como recomienda ismail ,

Dado que Perl está disponible en todas partes, solo lo hago perl -pi -e s,foo,bar,g target.file

y esto parece una mejor solución en casi cualquier caso que los scripts, los alias u otras soluciones para tratar la incompatibilidad fundamental sed -ientre GNU / Linux y BSD / Mac.

Alex Dupuy
fuente
Amo esta solución perles parte de la especificación de Linux Standard Base desde el 1 de mayo de 2009. refspecs.linuxfoundation.org/LSB_4.0.0/LSB-Languages/…
OregonTrail
1
Esta es fácilmente la mejor solución para la portabilidad
ecnepsnai
17

No hay forma de que funcione.

Una forma es usar un archivo temporal como:

TMP_FILE=`mktemp /tmp/config.XXXXXXXXXX`
sed -e "s/abc/def/" some/file > $TMP_FILE
mv $TMP_FILE some/file

Esto funciona en ambos

término análogo
fuente
¿Hay alguna razón por la que SOME_FILE = "$ (sed -s" s / abc / def / "some / file)"; echo "$ SOME_FILE"> algún / archivo; no funcionará en su lugar
Jason Gross
Parece que estarías limitado por el tamaño máximo de una variable bash, ¿no? No estoy seguro de que funcionará con archivos GB.
análogo
3
Si está creando un archivo temporal, ¿por qué no le da una extensión para crear un archivo de respaldo (que luego puede eliminar) como @kine sugiere a continuación?
Alex Dupuy
13

Respuesta: No.

La respuesta originalmente aceptada en realidad no hace lo que se solicita (como se señala en los comentarios). (Encontré esta respuesta cuando busqué la razón por la que file-eaparecía "al azar" en mis directorios).

Aparentemente no hay forma de sed -itrabajar de manera consistente tanto en MacOS como en Linuces.

Mi recomendación, por lo que vale, no es actualizar en el lugar sed(que tiene modos de falla complejos), sino generar nuevos archivos y cambiarles el nombre después. En otras palabras: evitar -i.

Steve Powell
fuente
9

La -iopción no es parte de POSIX Sed . Un método más portátil sería usar Vim en modo Ex:

ex -sc '%s/alfa/bravo/|x' file
  1. % seleccione todas las líneas

  2. s reemplazar

  3. x guardar y cerrar

Steven Penny
fuente
Esta es la respuesta correcta. Una característica clave de POSIX es la portabilidad.
Kajukenbo el
9

Aquí hay otra versión que funciona en Linux y macOS sin usar evaly sin tener que eliminar archivos de copia de seguridad. Utiliza matrices Bash para almacenar los sedparámetros, que es más limpio que usar eval:

# Default case for Linux sed, just use "-i"
sedi=(-i)
case "$(uname)" in
  # For macOS, use two parameters
  Darwin*) sedi=(-i "")
esac

# Expand the parameters in the actual call to "sed"
sed "${sedi[@]}" -e 's/foo/bar/' target.file

Esto no crea un archivo de copia de seguridad, ni un archivo con citas adjuntas.

nwinkler
fuente
2
Esta es la respuesta correcta. Lo simplificaría un poco, pero la idea funciona perfectamente sedi=(-i) && [ "$(uname)" == "Darwin" ] && sedi=(-i '') sed "${sedi[@]}" -e 's/foo/bar/' target.file
Alex Skrypnyk
¡Buen material! Sin embargo, prefiero la versión más larga para facilitar la lectura.
nwinkler
1
Esta es una mejor manera para que yo sea compatible con diferentes sistemas.
Victor Choy
6

La respuesta de Steve Powell es bastante correcta, al consultar la página MAN para sed en OSX y Linux (Ubuntu 12.04) destaca la incompatibilidad dentro del uso de sed 'in situ' en los dos sistemas operativos.

JFYI, no debería haber espacio entre el -i y las comillas (que denotan una extensión de archivo vacía) usando la versión Linux de sed, por lo tanto

sed Linux Man Page

#Linux
sed -i"" 

y

Página de manual de sed OSX

#OSX (notice the space after the '-i' argument)
sed -i "" 

Lo resolví en un script usando un comando alias'd y la salida del nombre del sistema operativo de ' uname ' dentro de un bash 'if'. Intentar almacenar cadenas de comandos dependientes del sistema operativo en variables fue impredecible al interpretar las comillas. El uso de ' shopt -s expand_aliases ' es necesario para expandir / usar los alias definidos en su script. El uso de shopt se trata aquí .

Grande rico
fuente
1

Si necesita hacer sedin situ en un bashscript, y NO desea que el lugar resulte con archivos .bkp, y tiene una manera de detectar el sistema operativo (por ejemplo, usando ostype.sh ), - entonces El siguiente truco con el bashshell incorporado evaldebería funcionar:

OSTYPE="$(bash ostype.sh)"

cat > myfile.txt <<"EOF"
1111
2222
EOF

if [ "$OSTYPE" == "osx" ]; then
  ISED='-i ""'
else # $OSTYPE == linux64
  ISED='-i""'
fi

eval sed $ISED 's/2222/bbbb/g' myfile.txt
ls 
# GNU and OSX: still only myfile.txt there

cat myfile.txt
# GNU and OSX: both print:
# 1111
# bbbb

# NOTE: 
# if you just use `sed $ISED 's/2222/bbbb/g' myfile.txt` without `eval`,
# then you will get a backup file with quotations in the file name, 
# - that is, `myfile.txt""`
sdaau
fuente
0

Puedes usar esponja. Sponge es un antiguo programa de Unix, que se encuentra en el paquete moreutils (tanto en ubuntu y probablemente debian, como en homebrew en mac).

Almacenará todo el contenido de la tubería, esperará hasta que la tubería esté cerrada (probablemente significa que el archivo de entrada ya está cerrado) y luego sobrescribirá:

Desde la página del manual :

Sinopsis

sed '...' archivo | grep '...' | archivo de esponja

Guillermo
fuente
3
Enfoque agradable: desafortunadamente, en realidad no ayuda en la situación original, ya que la razón para recurrir a sed era tener algo que usar en un script auxiliar multiplataforma utilizado en máquinas cliente, donde no puede depender de otra cosa que no sea el sistema herramientas instaladas. Sin embargo, podría ser útil para alguien más.
dnadlinger
0

Lo siguiente funciona para mí en Linux y OS X:

sed -i' ' <expr> <file>

por ejemplo, para un archivo que fcontieneaaabbaaba

sed -i' ' 's/b/c/g' f

cede aaaccaacatanto en Linux como en Mac. Tenga en cuenta que hay una cadena entre comillas que contiene un espacio , sin espacio entre-i y la cadena. Las comillas simples o dobles funcionan.

En Linux estoy usando la bashversión 4.3.11 bajo Ubuntu 14.04.4 y en la versión Mac 3.2.57 bajo OS X 10.11.4 El Capitan (Darwin 15.4.0).

David G
fuente
1
¡Guau, me alegro de no haber escuchado a todos los de arriba diciendo que era imposible y seguí leyendo! Esto realmente funciona. ¡Gracias!
Personman
44
No funciona para mí en Mac OS ... se crea un nuevo archivo con espacio agregado al final del nombre de archivo
Frédéric
Tampoco funciona para mí en Mac (/ usr / bin / sed): ahora tengo un archivo de copia de seguridad adicional con un espacio agregado al nombre del archivo :-(
paul_h
Intenta eliminar el espacio de las comillas simples, me funciona.
dps
0

Me encontré con este problema. La única solución rápida fue reemplazar el sed en mac a la versión gnu:

brew install gnu-sed
vikrantt
fuente
Esto duplica una respuesta existente con mucho más detalle de 2015.
tripleee