Rileva se uno strumento è già in esecuzione ma SOLO per l'utente corrente


8

Fino ad ora ho usato

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

Per rilevare il pid di un'istanza eventualmente in esecuzione di my-tool.

Questa è la versione breve del file my-tool, uno script bash eseguibile

#!/bin/bash 

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

 ... if not running go on 

Ma ora devo consentire una singola istanza per utente e più per macchina , quindi possiamo avere anche 100 my-tool in esecuzione nello stesso momento, ma solo 1 per utente.

Nota che ho bisogno di un test per creare qualcosa di simile a un singleton. Se lo strumento verrà avviato e c'è un'altra istanza in esecuzione, si chiuderà automaticamente.

In breve: ho bisogno che uno script bash possa rilevare se è già in esecuzione per l'utente corrente e in questo caso deve uscire.

Come ?


Non dovresti usare pidof, ci sono strumenti migliori mentirepgrep
cat

Risposte:


12

Usa pgrepinvece:

pgrep -cxu $USER -f my-tool

Le opzioni utilizzate sono:

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

Se vuoi usarlo in uno script bash che controlla se è già in esecuzione, puoi usarlo $0. Questo si espande nel percorso dello script corrente (ad es. /home/username/bin/foo.sh) Ma ne abbiamo solo bisogno foo.sh. Per ottenere questo, siamo in grado di rimuovere tutto ciò fino all'ultimo /usando del bash strumenti di manipolazione della stringa : ${0##*/}. Ciò significa che possiamo fare qualcosa del tipo:

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

Potresti anche considerare l'utilizzo di lockfile per questo:

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


1
Invece di touch /tmp/$USER.foo.lockutilizzare il PID del processo echo $$ >/tmp/$USER.foo.lock, è possibile includere la logica per verificare se tale processo esiste effettivamente.
Monty Harder,

@MontyHarder davvero, ottimo suggerimento. Risposta modificata, grazie.
terdon,

@terdon, ha avuto problemi con il cellulare, ha eliminato quel commento nel secondo successivo. Intendevo $(< pidfile)invece di $(cat pidfile).
sdkks,

7

È possibile utilizzare pgrepfo find se un processo viene eseguito da un utente specifico e quindi avviare il processo se non è già in esecuzione dall'utente:

#!/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 variabile d'ambiente $USER, verrà estesa all'utente attualmente connesso, ovvero all'utente che esegue lo script. Dato che ci interessa solo sapere se my-toolè in esecuzione o meno, è sufficiente utilizzare lo stato di uscita direttamente con il ifcostrutto.

Utilizzare questo script come wrapper per l'avvio my-toole fare in modo che gli utenti lo utilizzino solo o rinominarlo come my-toole rinominare l'originale my-toolin qualcos'altro (e modificare anche il nome all'interno dello script).


Non riesco a creare un wrapper, una lunga cronologia, ... come posso escludere l'attuale PID del processo?
Realtebo,

@realtebo ummm..exclude significato PID?
heemayl,

2

Provalo con questo frammento:

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

È importante non scrivere pgrep|trperché questo sarebbe fork in una shell con lo stesso nome.


2

Avvolgi il comando che desideri eseguire in un gregge per assicurarti che venga eseguita una sola copia alla volta. Utilizzare un file lock_file memorizzato nella struttura della directory home di un utente in modo che ogni utente abbia il proprio file di blocco. Per esempio:

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

'command' può essere qualsiasi comando o script bash.

man flock fornisce esempi di utilizzo


Davvero, non conoscevo affatto il flockcomando
realtebo
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.