¿Hay alguna alternativa al comando "sed -i" en Solaris?


11

Tengo un requisito en mi proyecto para reemplazar parte del texto existente en un archivo como foocon algún otro texto como fooofoo:

abc.txt
name
foo
foo1

Entonces intenté:

sed -i "s/foo/fooofoo/g" abc.txt

Sin embargo me sale este error:

sed: opción ilegal i

Encontré en el manual que tengo que usar:

sed -i\ "s/foo/fooofoo/g" abc.txt

Sin embargo, esto tampoco funciona.

Encontré alternativas perly awktambién, pero una solución en Solaris sedsería muy apreciada.

Estoy usando esta versión de bash:

GNU bash, versión 3.2.57 (1) -release (sparc-sun-solaris2.10)


En Solaris 11 y versiones posteriores, simplemente use /usr/gnu/bin/sedpara obtener soporte -i.
alanc

Respuestas:


15

Uso ed. Está disponible en la mayoría de las plataformas y puede editar sus archivos en el lugar.
Dado que sedse basa en edla sintaxis para reemplazar patrones es similar:

ed -s infile <<\IN
,s/old/new/g
w
q
IN

¿Por qué en edlugar de exsolo curiosidad? (Sé que ambos son compatibles con POSIX y me he vinculado a las especificaciones.))
Comodín el

1
@Wildcard: podría preguntar lo contrario, ¿por qué ex? Para responder a su pregunta: como nunca uso ex no estoy familiarizado con él. por cierto, he visto configuraciones donde edestaba presente pero exno estaba (pero no al contrario) ... el más notable es mi computadora portátil :)
don_crissti

Buena respuesta. :) Mi respuesta: como uso Vim todo el tiempo, estoy muy familiarizado con los excomandos. Todos los vicomandos que puede escribir que comienzan con dos puntos son excomandos. Por supuesto, hay algunas extensiones específicas de Vim, pero solo el conjunto central de comandos es increíblemente potente y flexible y es suficiente para las ediciones con script.
Comodín el

Mi sistema Manjaro tiene exy aún no ed.
mediados del

8

Si no puede instalar GNU sed, use:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Esto utiliza la redirección para enviar la salida de sed a un archivo temporal. Si sed se completa con éxito, esto se sobrescribe abc.txtcon el archivo temporal.

Como se puede ver en el código fuente de GNU sed , esto es exactamente lo que sed -ihace. Entonces, esto es casi tan eficiente como sed -i.

Si ya existe una posibilidad abc.tmp, es posible que desee utilizar mktempuna utilidad similar o similar para generar el nombre único para el temporal.


Gracias por la ayuda. Sin embargo, ¿hay alguna opción en Solaris por la cual podamos actualizar el archivo existente sin crear un archivo temporal?
tpsaitwal

10
Odio decírtelo, pero incluso sed crea un archivo temporal. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller

1
Puedes hacerlo si no te interesan los enlaces duros, mira mi ejemplo. Todavía hay un archivo temporal, pero está oculto (sin nombre). Sin un temporal, deberías usarlo ed, ya que lee todo el archivo en la memoria.
Toby Speight

3

Si quieres el equivalente de sed -i.bak, es bastante simple.

Considere este script para GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Simplemente podemos reemplazar la línea marcada con

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

Esto mueve el archivo existente para que sea una copia de seguridad y escribe un nuevo archivo.

Si queremos el equivalente de

sed -i -e "$script" "$dir"  # no backup

entonces es un poco más complejo. Podemos abrir el archivo para leerlo como entrada estándar, luego desvincularlo, antes de dirigir la salida de sed para reemplazarlo:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Hacemos esto en un sub-shell, para que nuestro stdin original todavía esté disponible después de esto. Es posible cambiar las entradas y volver sin un subshell, pero de esta manera me parece más claro.

Tenga en cuenta que tenemos cuidado de copiar primero, en lugar de crear un nuevo fooarchivo; esto es importante si el archivo se conoce por más de un nombre (es decir, tiene enlaces duros) y desea asegurarse de no romper los enlaces .


3

Primero, debe saber que el i\comando al que se refiere es para insertar una línea de texto, no para guardar el texto editado nuevamente en el archivo. No hay una forma de POSIX especificada para usar sedpara eso.

Lo que puede hacer es usar ex, que está especificado por POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

El xcomando es para "guardar y salir". También puedes usarlo wqpero xes más corto.

El %signo al comienzo del comando sustituto significa "Aplicar este comando a cada línea en el búfer". Podrías usar 1,$s/find/replace/gtambién.


Una diferencia importante entre exy sedes que sedes un editor de flujo . Solo funciona secuencialmente, línea por línea. exes mucho más flexible que eso, y de hecho puedes editar archivos de texto interactivos directamente en ex. Es el predecesor inmediato de vi.


1

Usando sedy sin archivo temporal visible :

Puede evitar crear un "archivo temporal" visible separado :

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Explicación

Los sistemas tipo Unix en realidad no eliminan el contenido del archivo del disco hasta que ambos estén desvinculados en el sistema de archivos y no se abran en ningún proceso. Entonces puede hacer exec 3<para abrir el archivo en el shell para leer en el descriptor de archivo 3, rmel archivo (que lo desvincula del sistema de archivos), y luego llamar sedcon el descriptor de archivo 3 utilizado como entrada.

Tenga en cuenta que esto es muy diferente de esto:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

La diferencia es que cuando lo hace en un comando, el shell simplemente abre el mismo archivo tanto para leer como para escribir con la opción de truncar el archivo, ya que sigue siendo el mismo archivo, pierde el contenido. Pero si lo abre para leerlo, luego rmabre el mismo nombre de ruta para escribir, en realidad está creando un nuevo archivo con el mismo nombre de ruta (pero en un nuevo inodo y ubicación de disco, ya que el original todavía está abierto): entonces los contenidos aún están disponibles.

Luego, una vez que haya terminado, puede cerrar el descriptor de archivo que abrió anteriormente (eso es lo que hace la exec 3<&-sintaxis especial), que libera el archivo original para que el sistema operativo pueda eliminar (marcar como no utilizado) su espacio en disco.

Advertencias

Hay algunas cosas a tener en cuenta sobre esta solución:

  1. Solo obtiene una "lectura" a través del contenido: no hay una forma portátil de que un shell "busque" de nuevo en el descriptor de archivo, por lo que una vez que un programa lee algunos de los contenidos, otros programas solo verán el resto del archivo. Y sedleerá todo el archivo.

  2. Hay una pequeña posibilidad de que su archivo original se pierda si su shell / script / sed se mata antes de que se haga.


Para ser claros, @TobySpeight ya publicó un ejemplo de esta solución al final de su respuesta, pero pensé que podría usar una elaboración más profunda.
mtraceur

Para el votante negativo: si bien estoy seguro de que solo dejar un voto negativo te hace sentir bien contigo mismo, agradecería que contribuyeras más al brindar comentarios sobre los problemas que tienes con esta respuesta, cómo se puede mejorar, etc. .
mtraceur

tal vez puedas usar perl -i
c4f4t0r

@ c4f4t0r: Perdón por la respuesta tardía: Sí, perl -ies otra buena opción. La pregunta solicitó específicamente una solución compatible con Solaris ' sed, en contraste con las soluciones con perly awkque mencionaron que ya se encuentran. Además, mi respuesta tenía como objetivo principal agregar algo que pensé que era útil saber y que faltaba en cualquiera de las otras respuestas.
mtraceur
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.