Guarde las modificaciones en su lugar con awk

135

Estoy aprendiendo awky me gustaría saber si hay una opción para escribir cambios en el archivo, similar a seddonde usaría la -iopción para guardar modificaciones en un archivo.

Entiendo que podría usar la redirección para escribir cambios. Sin embargo, ¿hay alguna opción awkpara hacer eso?

Deano
fuente
Consulte también serverfault.com/a/547331/313521 para obtener la respuesta más general a "editar un archivo en su lugar con redirección".
Comodín el
@Comodín. La solución allí es horriblemente frágil. No hay absolutamente ninguna garantía sobre el orden de los eventos, y el uso de esa solución podría truncar sus datos. Por otro lado, no puedo comentar en ese sitio directamente porque necesito 50 repeticiones en ese sitio para hacerlo. Nunca entenderé por qué SO fragmentado en Unix / Linux y el administrador del servidor, et al. OMI, eso fue un error.
William Pursell
@WilliamPursell, "no hay garantía en el orden de los eventos", eso es realmente falso. La única fragilidad que tiene la solución es si la longitud del contenido es mayor que la longitud máxima de un comando. El orden de los eventos, sin embargo, está garantizado.
Comodín el
@Wildcard ¿Qué estándar garantiza ese pedido?
William Pursell
@WilliamPursell está garantizado por la documentación de bash. Para otros proyectiles no lo sé. (Por cierto, si vincula su cuenta, tendrá un bono de asociación de 100 repeticiones y podrá hacer comentarios).
Comodín el

Respuestas:

142

En la última versión de GNU Awk (desde la versión 4.1.0 ), tiene la opción de edición de archivos "in situ" :

[...] La extensión "in situ", construida utilizando la nueva instalación, se puede utilizar para simular la sed -ifunción " " GNU . [...]

Ejemplo de uso:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

Para mantener la copia de seguridad:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
lind
fuente
1
@sudo_O - Gracias por la demostración "in situ". ¡Votó su respuesta!
lind
Parece que la opción puede haber sido eliminada? Con 4.1.3, tengo "-i includefile --include = includefile"
Keith Hughitt el
1
@Keith Tenía la misma pregunta. Acabo de probarlo y funciona en mi 4.1.3. inplaceen realidad es una biblioteca incluida de gawkacuerdo con la respuesta de iiSeymour , por lo que inplacees algo que se puede incluir como un includefile.
cxw
Una advertencia importante aquí: la matriz 'visto' se llenará con líneas duplicadas de TODOS los archivos incluidos en el comando. Entonces, si cada archivo tiene, por ejemplo, un encabezado común, se eliminará en cada archivo después del primero. Si, en cambio, desea tratar cada archivo de forma independiente, deberá hacer algo como para f en * .txt; do gawk -i inplace '! visto [$ 0] ++' "$ f"; hecho
Nick K9
136

A menos que tenga GNU awk 4.1.0 o posterior ...

No tendrá una opción como la -iopción de sed, así que en su lugar:

$ awk '{print $0}' file > tmp && mv tmp file

Nota: -ino es mágico, también está creando un archivo temporal que sedsolo lo maneja por usted.


A partir de GNU awk 4.1.0 ...

GNU awkagregó esta funcionalidad en la versión 4.1.0 (publicada el 10/05/2013) . No es tan sencillo como dar la -iopción como se describe en las notas publicadas:

La nueva opción -i (de xgawk) se usa para cargar archivos de biblioteca awk. Esto difiere de -f en que el primer argumento sin opción se trata como un script.

Debe utilizar el inplace.awkarchivo de inclusión incluido para invocar la extensión correctamente de la siguiente manera:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

La variable INPLACE_SUFFIXse puede usar para especificar la extensión de un archivo de respaldo:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Estoy feliz esta característica se ha añadido pero para mí, la implementación no es muy awkish como el poder proviene de la concisión del lenguaje y -i inplacees de 8 caracteres demasiado tiempo imo .

Aquí hay un enlace al manual para la palabra oficial.

Chris Seymour
fuente
¿No debería ser tu 'primer' ejemplo más como awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Tony Barganski el
Para mi sorpresa, a partir de abril de 2019, todavía en gawk 4.0.2. No dejes que nadie te diga tal y tal versión estará disponible.
John Lunzer el
Litte más corto awk '{print $0}' file | sponge fileusando spongepartir moreutils.
brablc
15

@sudo_O tiene la respuesta correcta .

Esto no puede funcionar:

someprocess < file > file

El shell realiza las redirecciones antes de pasar el control a algún proceso ( redirecciones ). La >redirección truncará el archivo a tamaño cero ( salida de redireccionamiento ). Por lo tanto, cuando se inicia algún proceso y desea leer el archivo, no hay datos para que lo lea.

Glenn Jackman
fuente
14

solo un pequeño truco que funciona

echo "$(awk '{awk code}' file)" > file
Yuri G.
fuente
¡Funciona de maravilla! ¿Pero es posible guardar el comando awk en variable y solo usarlo en tu ingenioso truco?
ashrasmun
13

Una alternativa es usar sponge:

awk '{print $0}' your_file | sponge your_file

Donde lo reemplaza '{print $0}'por su script awk y your_filepor el nombre del archivo que desea editar en su lugar.

sponge absorbe completamente la entrada antes de guardarla en el archivo.

Codoscopio
fuente
¿Qué tan estándar / portátil es la esponja?
Thomas
2
spongees parte de moreutils. Por lo tanto, no estará presente por defecto en la mayoría de los sistemas. Pero parece que al menos en spongesí mismo es lo suficientemente portátil y se puede ejecutar en casi todas partes.
MarSoft
1
La desventaja de esta solución en comparación con la teebasada en es que spongeleerá todo en la RAM antes de escribir, por lo tanto, se congelará en archivos grandes.
MarSoft
5

lo siguiente no funcionará

echo $(awk '{awk code}' file) > file

Esto debería funcionar

echo "$(awk '{awk code}' file)" > file
Flowmix Leonsio
fuente
3

En caso de que desee una solución solo para awk sin crear un archivo temporal y que se pueda usar con version! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file
Halcón
fuente
44
Pero, ¿almacena esto todo el archivo en la memoria? Considere un archivo de 20GB.
Amit Naidu
0

Usando tee

 awk '{awk code}' file | tee file

el teecomando tiene lugar y se ejecuta después de que el awkcomando haya finalizado debido a |.

shaiki siegal
fuente
55
Esto es incorrecto. Los dos comandos se ejecutan en paralelo y los datos se transmiten inmediatamente a través de la tubería. Cualquier archivo más grande que el búfer (8192 bytes en mi máquina) se truncará y perderá datos.
tripflag