GNU/Linux >> Tutoriels Linux >  >> Linux

Verrouiller correctement les scripts Shell ?

Parfois, vous devez vous assurer qu'une seule instance d'un script shell est en cours d'exécution en même temps.

Par exemple, une tâche cron exécutée via crond qui ne fournit pas
de verrouillage par elle-même (par exemple, le crond Solaris par défaut).

Un modèle courant pour implémenter le verrouillage est un code comme celui-ci :

#!/bin/sh
LOCK=/var/tmp/mylock
if [ -f $LOCK ]; then            # 'test' -> race begin
  echo Job is already running!
  exit 6
fi
touch $LOCK                      # 'set'  -> race end
# do some work
rm $LOCK

Bien sûr, un tel code a une condition de concurrence. Il y a une fenêtre de temps où l'exécution
de deux instances peut avancer toutes les deux après la ligne 3 avant que l'une d'entre elles puisse
toucher le $LOCK fichier.

Pour une tâche cron, ce n'est généralement pas un problème car vous avez un intervalle de
minutes entre deux invocations.

Mais les choses peuvent mal tourner – par exemple lorsque le fichier de verrouillage est sur un serveur NFS –
qui se bloque. Dans ce cas, plusieurs tâches cron peuvent se bloquer sur la ligne 3 et se mettre en file d'attente. Si
le serveur NFS est de nouveau actif, alors vous avez un troupeau impressionnant de travaux parallèles
en cours d'exécution.

En cherchant sur le Web, j'ai trouvé l'outil lockrun qui semble être une bonne
solution à ce problème. Avec lui, vous exécutez un script qui doit être verrouillé comme
ceci :

$ lockrun --lockfile=/var/tmp/mylock myscript.sh

Vous pouvez le mettre dans un wrapper ou l'utiliser depuis votre crontab.

Il utilise lockf() (POSIX) si disponible et revient à flock() (BSD). Et lockf() la prise en charge via NFS devrait être relativement répandue.

Existe-t-il des alternatives à lockrun ?

Qu'en est-il des autres démons cron ? Existe-t-il des crond communs qui prennent en charge le verrouillage de
manière sensée ? Un rapide coup d'œil à la page de manuel de Vixie Crond (par défaut sur
les systèmes Debian/Ubuntu) ne montre rien sur le verrouillage.

Serait-il judicieux d'inclure un outil comme lockrun dans coreutils ?

À mon avis, il implémente un thème très similaire à timeout , nice et amis.

Réponse acceptée :

Voici une autre façon de verrouiller le script shell qui peut empêcher la condition de concurrence que vous décrivez ci-dessus, où deux tâches peuvent toutes deux passer la ligne 3. Le noclobber l'option fonctionnera dans ksh et bash. Ne pas utiliser set noclobber car vous ne devriez pas écrire de script dans csh/tcsh. 😉

lockfile=/var/tmp/mylock

if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then

        trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT

        # do stuff here

        # clean up after yourself, and release your trap
        rm -f "$lockfile"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockfile owned by $(cat $lockfile)"
fi

YMMV avec verrouillage sur NFS (vous savez, lorsque les serveurs NFS ne sont pas accessibles), mais en général, il est beaucoup plus robuste qu'avant. (il y a 10 ans)

Si vous avez des tâches cron qui font la même chose en même temps, à partir de plusieurs serveurs, mais que vous n'avez besoin que d'une seule instance pour s'exécuter, quelque chose comme ça pourrait fonctionner pour vous.

En relation:simple commande MacOS Bluetooth Toggle Shell?

Je n'ai aucune expérience avec lockrun, mais avoir un environnement de verrouillage prédéfini avant l'exécution du script peut aider. Ou peut-être pas. Vous définissez simplement le test pour le fichier de verrouillage en dehors de votre script dans un wrapper, et théoriquement, ne pourriez-vous pas simplement atteindre la même condition de concurrence si deux tâches étaient appelées par lockrun exactement au même moment, tout comme avec le 'inside- la solution du script ?

De toute façon, le verrouillage de fichiers respecte à peu près le comportement du système, et tous les scripts qui ne vérifient pas l'existence du fichier de verrouillage avant de s'exécuter feront tout ce qu'ils vont faire. Rien qu'en mettant le test du fichier de verrouillage et en adoptant un comportement approprié, vous résoudrez 99 % des problèmes potentiels, voire 100 %.

Si vous rencontrez souvent des conditions de course de fichiers verrouillés, cela peut être un indicateur d'un problème plus important, comme le fait que vos travaux ne sont pas chronométrés correctement, ou peut-être que si l'intervalle n'est pas aussi important que l'achèvement du travail, peut-être que votre travail est mieux adapté pour être démonisé .

MODIFIER CI-DESSOUS - 2016-05-06 (si vous utilisez KSH88)

Basé sur le commentaire de @Clint Pachl ci-dessous, si vous utilisez ksh88, utilisez mkdir au lieu de noclobber . Cela atténue principalement une condition de concurrence potentielle, mais ne la limite pas entièrement (bien que le risque soit minime). Pour plus d'informations, lisez le lien que Clint a publié ci-dessous.

lockdir=/var/tmp/mylock
pidfile=/var/tmp/mylock/pid

if ( mkdir ${lockdir} ) 2> /dev/null; then
        echo $$ > $pidfile
        trap 'rm -rf "$lockdir"; exit $?' INT TERM EXIT
        # do stuff here

        # clean up after yourself, and release your trap
        rm -rf "$lockdir"
        trap - INT TERM EXIT
else
        echo "Lock Exists: $lockdir owned by $(cat $pidfile)"
fi

Et, comme avantage supplémentaire, si vous avez besoin de créer des fichiers temporaires dans votre script, vous pouvez utiliser le lockdir répertoire pour eux, sachant qu'ils seront nettoyés à la sortie du script.

Pour un bash plus moderne, la méthode noclobber en haut devrait convenir.


Linux
  1. Tableaux associatifs dans les scripts shell ?

  2. Exécuter des scripts Shell via un site Web ?

  3. Masquer le mot de passe dans les scripts shell ?

  4. Extensions de fichiers pour les scripts shell Unix ?

  5. Partage de variables sur plusieurs scripts shell ?

Comment créer des scripts shell

Tableaux dans les scripts shell

Comment utiliser if-else dans les scripts shell ?

Comprendre la boucle for dans les scripts shell

La boucle while dans les scripts shell

Exécutez tous les scripts shell dans le dossier