Effectuer des opérations d'écriture atomique dans un fichier en bash

13

Après avoir parcouru la documentation de bash , cette question et celle- ci ne me permettent toujours pas de savoir comment effectuer des opérations d'écriture atomique (ajouter) dans un fichier en bash. J'ai un script qui est exécuté dans plusieurs instances et à un moment donné, je dois écrire des données dans un fichier:

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

Comment est-il possible de rendre atomiques toutes les opérations d'écriture de tous les scripts exécutés simultanément (afin que les données d'une instance ne chevauchent pas les données d'une autre)?

Sebi
la source
1
Pour info vous n'avez pas besoin `pwd`; vous pouvez simplement utiliser un point ( .). Vous devez également citer ce nom de fichier entier car il comprend des variables .
Wildcard
1
Vous pouvez également envisager la possibilité d' utiliser des FIFO .
Wildcard
@Wildcard Merci. J'utilisais pwdplus tôt dans le script pour informer l'utilisateur du répertoire de travail actuel et également écrire des entrées dans un fichier journal. regardant maintenant les FIFO.
Sebi
1
En fait, il y a une meilleure introduction aux FIFO directement sur ce site même; le lien que j'ai fourni il y a quelques minutes est les spécifications POSIX mkfifoet pas exactement le niveau d'introduction.
Wildcard
Qu'il s'agisse d'un FIFO ou d'un fichier, vous courez toujours le risque que deux instances écrivent en même temps et brouillent les sorties de l'autre.
clacke

Réponses:

10

Il semble que vous devez utiliser flockcomme dans l'exemple 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 

Et mettez toutes vos commandes qui doivent être atomiques dans ().


la source
L'opération d'écriture sera atomique même si (flock -s 200 # Mettez ici vos commandes qui doivent faire des écritures atomiquement) 200> / var / lock / mylockfile est lu sur plusieurs scripts exécutés simultanément? Ou, inversement, le verrou est-il uniquement relatif à / var / lock / mylockfile?
Sebi
1
Votre script verrouille / var / lock / mylockfile, mais une seule instance de votre script peut obtenir un verrou en écriture dessus. D'autres instances attendront jusqu'à ce /var/lock/mylockfileque le verrouillage soit disponible.
-s est un verrou partagé - adapté aux lectures, pour un verrou en écriture, vous devez utiliser -x / -e. C'est dans la page de manuel que vous avez liée.
Evan Benn
À quel moment le déverrouillage se produit-il dans ce code? Dès que quelque chose est écrit dans le /var/lock/mylockfilepar le processus qui a appelé flock -x?
Chris Stryczynski
@ChrisStryczynski Vous pouvez exécuter cet exemple (flock -x 200; date; sleep 10; date;) 200>/var/lock/mylockfiledans deux shells et voir que les déverrouillages ne se produisent qu'après l'exécution de toutes les commandes dans () (c'est-à-dire dans un sous-shell)
4

flockest l'un des moyens de verrouillage des opérations. L'utilitaire fait partie du jeu d'outils util-linux et n'est disponible que pour Linux. D'autres utilitaires, disponibles sur une plus large gamme de plates-formes, sont basés sur l' setlockutilitaire de Daniel J. Bernstein à partir de son package daemontools:

Ces outils fonctionnent avec un paradigme légèrement différent de celui utilisé dans la réponse de M. Kurenkov (un qui flockpeut également utiliser, mais pas dans cette réponse). On appelle le setlockprogramme pour chaîner la charge à la commande qui doit être verrouillée. setlocklui-même ouvre et verrouille le fichier de verrouillage et laisse un descripteur de fichier ouvert dans son processus. Le verrou persiste aussi longtemps que ce processus le fait (sauf si la commande suivante chaînée pour libérer explicitement le verrou en recherchant et en fermant le descripteur de fichier ouvert).

Pour le cas de la question, il faut verrouiller la commande qui produit la ligne de sortie, sachant que cela appelle un externe echo à la place d'une echocommande intégrée au shell :

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

Dans ce cas, il n'est pas nécessaire de verrouiller l'ouverture du fichier de sortie en mode ajout. Si c'était le cas, il faudrait ouvrir ce fichier dans le verrou, ce qui nécessite soit l'utilisation de programmes comme fdredir/ redirfd:

setlock mylockfile fdredir - append 1 "./$TEMP_DIR/$OUT_FILE" echo "$ RESULT"
que l'on peut transformer en fonction shell si l'on veut:

outfile () {setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" "$ @"; } 
[…]
Écho de fichier "$ RESULT"
ou s'en tenir à la syntaxe du shell et la faire interpréter par un second shell exécuté sous le verrouillage, nécessitant des citations non triviales si ses variables de shell ne sont pas exportées en tant que variables d'environnement:

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

Bien sûr, cela se généralise à autre chose que l'écriture dans des fichiers de sortie:

setlock mylockfile sh -c '… verrouillé; des trucs …'

JdeBP
la source