Sprawdź, czy narzędzie jest już w trakcie wykonywania, ale TYLKO dla bieżącego użytkownika

8

Do tej pory korzystałem

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

Aby wykryć pid ostatecznie działającej instancji mojego narzędzia.

To jest krótka wersja mojego narzędzia, wykonywalnego skryptu bash

#!/bin/bash 

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

 ... if not running go on 

Ale teraz muszę zezwolić na jedną instancję na użytkownika i wiele na maszynę , abyśmy mogli uruchomić nawet 100 moich narzędzi w tym samym momencie, ale tylko 1 na użytkownika.

Zauważ, że potrzebuję testu, aby stworzyć coś w stylu singletona. Jeśli narzędzie zostanie uruchomione i uruchomiona zostanie inna instancja, zostanie ono zamknięte.

Krótko mówiąc: potrzebuję, aby skrypt bash mógł wykryć, czy sam już działa dla bieżącego użytkownika i w tym przypadku musi wyjść.

Jak ?

realtebo
źródło
Nie powinieneś używać pidof, są lepsze narzędzia do kłamstwapgrep
kot

Odpowiedzi:

12

pgrepZamiast tego użyj :

pgrep -cxu $USER -f my-tool

Dostępne opcje to:

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

Jeśli chcesz użyć tego w skrypcie bash, który sprawdza, czy jest już uruchomiony, możesz użyć $0. Rozszerza się to na ścieżkę bieżącego skryptu (np. /home/username/bin/foo.sh), Ale potrzebujemy tylko foo.sh. Aby dostać się, że możemy usunąć wszystko aż do ostatniego /Przy użyciu bash za narzędzi manipulacji ciąg : ${0##*/}. Oznacza to, że możemy zrobić coś takiego:

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

Możesz również rozważyć użycie do tego celu plików blokujących:

## 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
źródło
1
Zamiast touch /tmp/$USER.foo.lockużywać PID procesu, echo $$ >/tmp/$USER.foo.lockmożna by zastosować logikę, aby sprawdzić, czy taki proces faktycznie istnieje.
Monty Harder
@MontyHarder rzeczywiście, bardzo dobra sugestia. Odpowiedź edytowana, dzięki.
terdon
@terdon, miał problem z telefonem komórkowym, usunął ten komentarz w następnej sekundzie. Miałem na myśli $(< pidfile)zamiast $(cat pidfile).
sdkks
7

Możesz użyć do pgrepsprawdzenia, czy proces jest wykonywany przez określonego użytkownika, a następnie uruchom proces, jeśli nie jest już uruchomiony przez użytkownika:

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

Zmienna środowiskowa $USER, zostanie rozwinięta do aktualnie zalogowanego użytkownika, tj. Użytkownika uruchamiającego skrypt. Ponieważ interesuje nas tylko to, czy my-tooljest uruchomiony, czy nie, wystarczy samo użycie statusu wyjścia bezpośrednio z ifkonstruktem.

Użyj tego skryptu jako opakowania do uruchomienia my-tooli spraw, aby użytkownicy używali go tylko lub zmień jego nazwę jako my-tooli zmień nazwę oryginału my-toolna coś innego (i zmień nazwę również w skrypcie).

heemayl
źródło
Nie mogę zrobić otoki, długiej historii ... jak mogę wykluczyć PID bieżącego procesu?
realtebo
@realtebo ummm .. wykluczyć znaczenie PID?
heemayl
2

Wypróbuj za pomocą tego fragmentu:

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

Ważne jest, aby nie pisać, pgrep|trponieważ rozwinęłoby się to w powłokę o tej samej nazwie.

rexkogitans
źródło
2

Zawiń polecenie, które chcesz uruchomić, w stado, aby mieć pewność, że jednocześnie działa tylko jedna kopia. Użyj pliku blokady zapisanego w drzewie katalogu domowego użytkownika, aby każdy użytkownik miał własny plik blokady. Na przykład:

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

„polecenie” może być dowolnym poleceniem lub skryptem bash.

man flock podaje przykłady użycia

gość
źródło
Naprawdę, w ogóle nie znałem flockpoleceń
realtebo