Détecter si un outil est déjà en cours d'exécution mais UNIQUEMENT pour l'utilisateur actuel

8

Jusqu'à présent, j'ai utilisé

pidof -o %PPID -x "my-tool"

Pour détecter le pid d'une instance éventuellement en cours d'exécution de my-tool.

Ceci est la version courte du fichier my-tool, un script bash exécutable

#!/bin/bash 

if pidof -o %PPID -x "my-tool"; then
   echo "Already running"
   exit 1
fi

 ... if not running go on 

Mais maintenant, je dois autoriser une seule instance par utilisateur et plusieurs par machine , afin que nous puissions même avoir 100 my-tool en cours d'exécution au même moment, mais seulement 1 par utilisateur.

Notez que j'ai besoin d'un test pour créer quelque chose comme un singleton. Si l'outil est démarré et qu'une autre instance est en cours d'exécution, il se ferme automatiquement.

En bref: j'ai besoin qu'un script bash puisse détecter s'il s'exécute déjà pour l'utilisateur actuel et dans ce cas, il doit quitter.

Comment ?

realtebo
la source
Vous ne devriez pas utiliser pidof, il y a de meilleurs outils pour mentirpgrep
chat

Réponses:

12

Utilisez pgrepplutôt:

pgrep -cxu $USER -f my-tool

Les options utilisées sont:

   -c, --count
          Suppress  normal  output; instead print a count of matching pro
          cesses.  When count does not match anything, e.g. returns  zero,
          the command will return non-zero value.
   -x, --exact
          Only match processes whose names (or command line if -f is spec
          ified) exactly match the pattern.
   -u, --euid euid,...
          Only match processes whose effective user ID is listed.   Either
          the numerical or symbolical value may be used.

Si vous souhaitez l'utiliser dans un script bash qui vérifie s'il est déjà en cours d'exécution, vous pouvez l'utiliser $0. Cela se développe sur le chemin du script actuel (par exemple /home/username/bin/foo.sh) mais nous n'en avons besoin que foo.sh. Pour obtenir cela, nous pouvons tout enlever jusqu'au dernier à l' /aide de bash outils de manipulation de chaînes : ${0##*/}. Cela signifie que nous pouvons faire quelque chose comme:

## If there are more than 1 instances of the current script run
## by this user
if [[ $(pgrep -cxu "$USER" "${0##*/}") -gt 1 ]];
then
        echo "Script already running, exiting."
        exit
fi

Vous pouvez également envisager d'utiliser des fichiers de verrouillage pour cela:

## If the lock file exists
if [ -e /tmp/$USER.foo.lock ]; then
    ## Check if the PID in the lockfile is a running instance
    ## of foo.sh to guard against crashed scripts
    if ps $(cat /tmp/$USER.foo.lock) | grep foo.sh >/dev/null; then
        echo "Script foo.sh is already running, exiting"
        exit
    else
        echo "Lockfile contains a stale PID, continuing"
        rm /tmp/$USER.foo.lock 
    fi
fi
## Create the lockfile by printing the script's PID into it
echo $$ > /tmp/$USER.foo.lock

## Rest of the script here

## At the end, delete the lockfile
rm /tmp/$USER.foo.lock
terdon
la source
Continuons cette discussion dans le chat .
terdon
1
Plutôt que d' touch /tmp/$USER.foo.lockutiliser le PID du processus echo $$ >/tmp/$USER.foo.lock, une logique pourrait alors être incluse pour tester si un tel processus existe réellement.
Monty Harder
@MontyHarder en effet, très bonne suggestion. Réponse modifiée, merci.
terdon
@terdon, a rencontré un problème avec le mobile, a supprimé ce commentaire dans la seconde suivante. Je voulais dire $(< pidfile)au lieu de $(cat pidfile).
sdkks
7

Vous pouvez utiliser pgrepfo find si un processus est en cours d'exécution par un utilisateur spécifique, puis démarrer le processus s'il n'est pas déjà exécuté par l'utilisateur:

#!/bin/bash
if pgrep -u "$USER" my-tool &>/dev/null; then
    echo 'You already have my-tool running'
else
    /path/to/my_tool
fi

La variable d'environnement,, $USERsera étendue à l'utilisateur actuellement connecté, c'est-à-dire l'utilisateur exécutant le script. Comme nous voulons seulement savoir si le my-toolest en cours d'exécution ou non, il suffit donc d'utiliser directement l'état de sortie avec la ifconstruction.

Utilisez ce script comme un wrapper pour démarrer my-toolet faire en sorte que les utilisateurs l'utilisent uniquement ou renommez-le en my-toolrenommant l'original my-toolen quelque chose d'autre (et changez également le nom dans le script).

heemayl
la source
Je ne peux pas créer un wrapper, longue histoire, ... comment puis-je exclure le processus PID actuel?
realtebo
@realtebo ummm..exclude PID signification?
heemayl
2

Essayez-le avec cet extrait:

#!/bin/bash
MyProcessName=$(ps -p $$ -o args=)
Mypid=$$
AllPids=$(pgrep -fu "$(whoami)" "$MyProcessName")
AllPids=$(tr "\n" ' ' <<<"$AllPids")
Pids=$(sed "s/$Mypid//" <<<"$AllPids")
echo "$$: Instances including itself: $AllPids"
echo "$$: Instances different from itself: $Pids"

Il est important de ne pas écrire pgrep|trcar cela déboucherait sur un même shell nommé.

rexkogitans
la source
2

Enveloppez la commande que vous souhaitez exécuter dans un troupeau pour vous assurer qu'une seule copie s'exécute à la fois. Utilisez un fichier lock_file stocké dans l'arborescence du répertoire personnel d'un utilisateur afin que chaque utilisateur dispose de son propre fichier de verrouillage. Par exemple:

lockfile = "~/lockfile"
(
 if flock -n 200; then
    command
 else
    echo "Could not get lock $lock_file
 fi
) 200>$lock_file

'commande' peut être n'importe quelle commande ou script bash.

man flock donne des exemples d'utilisation

un invité
la source
Vraiment, je ne connaissais pas du tout le flockcommandement
realtebo