Realizar operaciones de escritura atómica en un archivo en bash

13

Después de revisar la documentación de bash , esta pregunta y esta todavía no me queda claro cómo puedo realizar operaciones de escritura atómica (agregar) a un archivo en bash. Tengo un script que se ejecuta en varias instancias y en algún momento debo escribir datos en un archivo:

echo "$RESULT" >> `pwd`/$TEMP_DIR/$OUT_FILE

¿Cómo es posible hacer que todas las operaciones de escritura de todos los scripts que se ejecutan simultáneamente en ese archivo sean atómicas (para que los datos de una instancia no se superpongan con los de otra)?

Sebi
fuente
1
FYI no necesitas `pwd`; puedes usar un punto ( .). También debe citar ese nombre de archivo completo ya que incluye variables .
Comodín el
1
También puede considerar la posibilidad de utilizar FIFO .
Comodín el
@Wildcard Gracias. Estaba usando pwdanteriormente en el script para notificar al usuario sobre el directorio de trabajo actual y también escribir entradas en un archivo de registro. mirando por encima de FIFOs ahora.
Sebi
1
En realidad, hay una mejor introducción a los FIFO en este mismo sitio; El enlace que proporcioné hace unos minutos son las especificaciones POSIX mkfifoy no exactamente el nivel introductorio.
Comodín el
Ya sea un FIFO o un archivo, aún corre el riesgo de que dos instancias escriban al mismo tiempo y empañen las salidas de cada uno.
clacke

Respuestas:

10

Parece que necesita usar flockcomo en el ejemplo de man ( http://linux.die.net/man/1/flock )

(
flock -x 200

# Put here your commands that must do some writes atomically

) 200>/var/lock/mylockfile 

Y ponga todos sus comandos que deben ser atómicos en ().


fuente
La operación de escritura será atómica aunque (flock -s 200 # Ponga aquí sus comandos que deben hacer algunas escrituras atómicamente) 200> / var / lock / mylockfile se lee en varios scripts que se ejecutan simultáneamente. O, por el contrario, ¿el bloqueo solo está relacionado con / var / lock / mylockfile?
Sebi
1
Su script bloquea / var / lock / mylockfile, pero solo una instancia de su script puede obtener un bloqueo de escritura. Otras instancias esperarán hasta que /var/lock/mylockfileesté disponible para el bloqueo.
-s es un bloqueo compartido: adecuado para lecturas, para un bloqueo de escritura debe usar -x / -e. Está en la página del manual que ha vinculado.
Evan Benn
¿En qué punto se produce el desbloqueo en este código? ¿Tan pronto como algo se escribe en /var/lock/mylockfileel proceso que llamó flock -x?
Chris Stryczynski
@ChrisStryczynski Puede ejecutar este ejemplo (flock -x 200; date; sleep 10; date;) 200>/var/lock/mylockfileen dos shells y ver que los desbloqueos ocurren solo después de que se ejecutan todos los comandos en () (es decir, en una subshell)
4

flockEs una de las formas de entrelazar operaciones. La utilidad es parte del conjunto de herramientas util-linux y solo está disponible para Linux. Otras utilidades, disponibles en una gama más amplia de plataformas, se basan en la setlockutilidad de Daniel J. Bernstein de su paquete daemontools:

  • setlock de daemontools
  • setlock de daemontools-encore de Bruce Guenter
  • s6-setlock del s6 de Laurent Bercot
  • chpst del runit de Gerrit Pape
  • runlock del asesino de Wayne Marshall
  • setlock de mi conjunto de herramientas nosh

Estas herramientas operan con un paradigma ligeramente diferente al utilizado en la respuesta de M. Kurenkov (una que flocktambién puede emplearse, pero no en esa respuesta). Uno invoca el setlockprograma para encadenar la carga al comando que debe estar enclavado. setlockse abre y bloquea el archivo de bloqueo, y deja un descriptor de archivo abierto en su proceso. El bloqueo persiste durante ese proceso (a menos que el comando posterior encadenado para liberar explícitamente el bloqueo al encontrar y cerrar el descriptor de archivo abierto).

Para el caso en la pregunta, uno debe enclavar el comando que produce la línea de salida, teniendo en cuenta que esto invoca un comando externo echo en lugar de un echocomando incorporado de shell :

setlock mylockfile echo "$ RESULT" >> ./$TEMP_DIR/$OUT_FILE

En este caso, no es necesario enclavar abriendo el archivo de salida en modo anexar. Si lo fuera, uno tendría que abrir ese archivo dentro del candado, lo que requiere usar programas como fdredir/ redirfd:

setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" echo "$ RESULT"
cuál se puede convertir en una función de shell si se quiere:

outfile () {setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" "$ @"; } 
[...]
outfile echo "$ RESULTADO"
o apegarse a la sintaxis de shell y hacer que sea interpretada por un segundo shell que se ejecuta bajo el enclavamiento, lo que requiere algunas citas no triviales si las variables de shell de uno no se exportan como variables de entorno:

setlock mylockfile sh -c 'echo' "$ RESULT" '>> "./'$TEMP_DIR'/'$OUT_FILE'" '

Por supuesto, esto se generaliza a otras cosas además de escribir en archivos de salida:

setlock mylockfile sh -c '... enclavado; cosas ...

JdeBP
fuente