Detecta si una herramienta ya está en ejecución pero SOLO para el usuario actual

8

Hasta ahora solía

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

Para detectar el pid de una instancia eventualmente en ejecución de my-tool.

Esta es la versión corta del archivo my-tool, un script bash ejecutable

#!/bin/bash 

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

 ... if not running go on 

Pero ahora necesito permitir una sola instancia por usuario y múltiples por máquina , por lo que podemos tener incluso 100 mi herramienta ejecutándose en el mismo momento, pero solo 1 por usuario.

Tenga en cuenta que necesito una prueba para crear algo así como un singleton. Si la herramienta se iniciará y hay otra instancia ejecutándose, se cerrará sola.

En resumen: necesito que un script bash pueda detectar si ya se está ejecutando para el usuario actual y, en este caso, debe salir.

Cómo ?

Realtebo
fuente
No deberías usar pidof, hay mejores herramientas mentirapgrep
gato

Respuestas:

12

Use en su pgreplugar:

pgrep -cxu $USER -f my-tool

Las opciones utilizadas son:

   -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 desea usar esto en un script bash que verifique si ya se está ejecutando, puede usarlo $0. Esto se expande a la ruta del script actual (p /home/username/bin/foo.sh. Ej. ) Pero solo necesitamos foo.sh. Para conseguir eso, podemos eliminar todo hasta el último /utilizando golpe de herramientas de manipulación de cadenas : ${0##*/}. Esto significa que podemos hacer algo como:

## 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

También es posible que desee considerar el uso de archivos de bloqueo para esto:

## 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
fuente
Continuemos esta discusión en el chat .
terdon
1
En lugar de touch /tmp/$USER.foo.lockusar el PID del proceso, echo $$ >/tmp/$USER.foo.lockentonces podría incluirse la lógica para probar si tal proceso realmente existe.
Monty Harder
@MontyHarder de hecho, muy buena sugerencia. Respuesta editada, gracias.
terdon
@terdon, tuvo problemas con el móvil, eliminó ese comentario en el siguiente segundo. Quise decir en $(< pidfile)lugar de $(cat pidfile).
sdkks
7

Puede usar pgrepfo find si un proceso está siendo ejecutado por un usuario específico y luego iniciar el proceso si el usuario aún no lo está ejecutando:

#!/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 de entorno $USER, se expandirá al usuario actualmente conectado, es decir, el usuario que ejecuta el script. Como solo estamos interesados ​​en si se my-toolestá ejecutando o no, basta con usar el estado de salida directamente con la ifconstrucción.

Use este script como envoltorio para comenzar my-tooly haga que los usuarios lo usen solo o renómbrelo como my-tooly cambie el nombre del original my-toola otro (y cambie el nombre dentro del script también).

heemayl
fuente
No puedo hacer un contenedor, una larga historia, ... ¿cómo puedo excluir el PID del proceso actual?
realtebo
@realtebo ummm..excluye el significado de PID?
heemayl
2

Pruébalo con este fragmento:

#!/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"

Es importante no escribir pgrep|trporque esto se bifurcaría en un mismo shell con nombre.

rexkogitans
fuente
2

Envuelva el comando que desea ejecutar en una bandada para asegurarse de que solo se ejecute una copia a la vez. Utilice un archivo lock_file almacenado dentro del árbol de directorio de inicio de un usuario para que cada usuario tenga su propio archivo de bloqueo. Por ejemplo:

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

'comando' puede ser cualquier comando bash o script.

man flock da ejemplos de uso

un invitado
fuente
Realmente, no sabía nada del flockcomando
realtebo