bash - script - Usar Awk o Sed para virar en una declaración al final de una línea específica
manejo de cadenas en shell script (3)
Dado que la utilidad de ed veteranos ya no recibe suficiente atención:
a=5.3827
ed -s poscar1.cif <<EOF
g/^_cell_length_a/$/ s//& $a/
w
EOF
ed
realmente edita un archivo en su lugar , a diferencia de sed
con su opción -i
[1] .
sed
tomó prestada muchas características de ed
, por lo que hay una superposición significativa en la funcionalidad, pero también hay diferencias importantes, algunas de las cuales aparecen aquí.
-
-s
suprime los mensajes de estado deed
. -
poscar1.cif
es el archivo de entrada para editar en su lugar. -
<<EOF ...
es el documento aquí que contiene los comandos paraed
-ed
requiere que sus comandos provengan de stdin y que cada comando esté en su propia línea. -
g/^_cell_length_a/$/
... es una expresión regular (básica) que coincide con todas las líneas que contienen exactamente_cell_length_a
- lag
garantiza que no se informa ningún error si no hay ninguna coincidencia.- Tenga en cuenta que el
$
es/
-capa para protegerlo de la interpretación por parte del shell dentro del documento aquí (no estrictamente necesario en este caso, pero sí buenas prácticas).
- Tenga en cuenta que el
-
s//& $a/
...//
repite la búsqueda de la expresión regular utilizada más recientemente en una línea coincidente y reemplaza la coincidencia consigo misma (&
), seguida de un espacio y el valor de la variable$a
.- Tenga en cuenta que dado que el delimitador de apertura (
EOF
) del documento aquí no está citado , las expansiones de variable de shell SI tienen lugar; en esencia, los contenidos son tratados por el intérprete de comandos como el contenido de una cadena de comillas dobles.
- Tenga en cuenta que dado que el delimitador de apertura (
-
w
escribe el búfer modificado de nuevo en el archivo de entrada.- Para la depuración, use
,p
en lugar dew
para imprimir solo el búfer modificado, sin volver a escribirlo en el archivo.
- Para la depuración, use
[1] Actualización in situ:
Más precisamente, ed
conserva el inodo existente del archivo, lo que garantiza que se conserven todos los atributos del archivo.
Sin embargo, no sobrescribe bytes individuales del archivo existente, sino que lee el archivo completo en un búfer en la memoria y escribe todo el búfer en el archivo cuando se le solicita.
Esto hace que ed
sea adecuado solo para archivos lo suficientemente pequeños como para leerlos en la memoria como un todo .
Por el contrario, sed -i
( GNU y BSD sed
), su contraparte GNU 4.1+, awk -i inplace
, y también perl -i
reemplazan el archivo original por uno nuevo , lo que implica que:
- destruir enlaces simbólicos (!) - si el archivo de entrada fue un enlace simbólico , se reemplaza con un archivo común del mismo nombre
- Un escenario común en el que eso importa: digamos que el archivo de inicialización de shell
~/.bashrc
es un enlace simbólico a un archivo en otro lugar que se mantiene bajo control de fuente; luego instala una herramienta que usased -i
para modificar~/.bashrc
, lo que da como resultado que se sustituya por un archivo normal, y el enlace a su versión controlada por código fuente se rompe. - Además, el comportamiento de BSD
sed
incluso presenta un riesgo de seguridad (ver más abajo).
- Un escenario común en el que eso importa: digamos que el archivo de inicialización de shell
- no conservar la fecha original de creación del archivo (cuando sea compatible, por ejemplo, en OSX)
lo hacen , sin embargo,
- conservar los atributos extendidos (cuando sea compatible, por ejemplo, en OSX)
preservar los permisos de archivos
- Precaución: BSD
sed
introduce un riesgo de seguridad con respecto a los enlaces simbólicos (el comportamiento aún está presente a partir de la versión que viene con FreeBSD 10):- Los permisos del enlace simbólico se copian en el archivo de reemplazo, no en el destino del enlace simbólico. Como los enlaces simbólicos obtienen permisos ejecutables de manera predeterminada, invariablemente se terminará con un archivo ejecutable , ya sea que el archivo de entrada sea ejecutable o no.
- Afortunadamente, GNU
sed
maneja este escenario correctamente.
- Precaución: BSD
sed
, gawk
y perl
podrían abordar los problemas anteriores mediante la adopción de medidas adicionales, pero hay una cosa que solo se puede garantizar si se retiene el inodo original, como lo hace ed
:
Cuando un archivo está siendo monitoreado por cambios por su número de inodo (por ejemplo, con tail -f
), no preservar el inode rompe esa monitorización.
Tengo un archivo que poscar1.cif
y me gustaría insertar el contenido de una variable en una línea específica de este archivo.
Por ejemplo, la línea 24
, que actualmente dice:
_cell_length_a
Me gustaría virar el contenido de mi variable a
(definida en mi función como a=5.3827
) de modo que la línea ahora lea:
_cell_length_a 5.3827
¿Hay alguna manera de hacer esto usando sed o awk? Estoy usando un script bash para lograr esto (el script completo es demasiado grande para publicarlo, desafortunadamente).
Puede usar sed para hacerlo, dependiendo de su respuesta a la pregunta de dawg como
sed -i -e ''24s/$/5.3827/'' poscar1.cif
o si es el patrón
sed -i -e ''/_cell_length_a/s/$/5.3827/'' poscar1.cif
El primero va a la línea con el número dado, el último se aplicará en cualquier línea que coincida con el patrón en el primer conjunto de barras diagonales. En cualquier caso, "reemplazará" el final de la línea con el valor entre las dos barras finales.
Usando tu ejemplo, podrías hacer algo como esto:
sed -i ''s//(_cell_length_a/)//1 5.3827/'' poscar1.cif
dónde,
- la opción
-i
dice que se edite el archivo en su lugar, en lugar de crear una copia - la parte citada de aspecto funky es una cadena que especifica una expresión regular, también conocida como regex
-
poscar1.cif
es el archivo
La sintaxis de expresiones regulares es difícil de leer. El formato básico para buscar y reemplazar es:
s/find/replace/
Donde find
es el texto de la línea que está buscando y replace
es el texto para reemplazar ese texto.
Si queremos usar parte de la cadena de búsqueda en nuestro reemplazo, la agrupamos rodeándola con /(
y /)
y luego usamos /1
para referirnos a ella en la cadena de reemplazo. Los siguientes anexos reemplazan a cualquier línea que consiste en find:
s//(find/)//1replace/
Tenga en cuenta que hay caracteres especiales de escape o metacaracteres que debe tratar especialmente si la cadena los contiene.