Визначте, чи інструмент вже виконується, але ТІЛЬКИ для поточного користувача


8

До цього часу я користувався

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

Для виявлення pid з часом запущеного екземпляра my-tool.

Це коротка версія файлу мого інструменту, виконуваний скрипт bash

#!/bin/bash 

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

 ... if not running go on 

Але тепер мені потрібно дозволити один екземпляр на користувача та декілька на машині , тож у нас може бути навіть 100 моїх інструментів, що працюють в той же момент, але лише 1 на кожного користувача.

Зауважте, що мені потрібен тест, щоб створити щось на зразок сингтона. Якщо інструмент буде запущений, а запущений інший екземпляр, він закриється сам.

Коротше кажучи: мені потрібно, щоб скрипт bash міг виявити, чи сам він вже працює для поточного користувача, і в цьому випадку він повинен вийти.

Як ?


Не слід користуватися pidof, є кращі інструменти брешутьpgrep
кіт

Відповіді:


12

Використовуйте pgrepзамість цього:

pgrep -cxu $USER -f my-tool

Використовувані варіанти:

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

Якщо ви хочете використовувати це в скрипті bash, який перевіряє, чи він вже запущений, ви можете використовувати $0. Це розширюється до шляху поточного сценарію (наприклад /home/username/bin/foo.sh), але нам це потрібно лише foo.sh. Для того, щоб отримати , що ми можемо видалити все до останнього , /використовуючи в Bash маніпуляції рядки інструментів : ${0##*/}. Це означає, що ми можемо зробити щось на кшталт:

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

Ви також можете розглянути можливість використання для цього замкових файлів:

## 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
Замість того, щоб touch /tmp/$USER.foo.lockя використовував PID процесу, echo $$ >/tmp/$USER.foo.lockтоді можна було б включити логіку, щоб перевірити, чи існує такий процес насправді.
Monty Harder

@MontyHarder дійсно, дуже гарна пропозиція. Відповідь відредаговано, спасибі
тердон

@terdon, мав проблеми з мобільним, видалив цей коментар у наступній секунді. Я мав на увазі $(< pidfile)замість $(cat pidfile).
sdkks

7

Ви можете використовувати pgrepfo find, якщо процес виконується певним користувачем, а потім запустити процес, якщо користувач вже не працює:

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

Змінна середовища $USER, буде розширена на поточно зареєстрованого користувача, тобто користувача, що виконує сценарій. Оскільки нас цікавить лише те my-tool, працює чи ні, то достатньо просто використовувати статус виходу безпосередньо з ifконструктом.

Використовуйте цей скрипт в якості обгортки для запуску my-toolі змусьте користувачів використовувати це лише або перейменувати його як my-toolі перейменувати оригінал my-toolна щось інше (і змінити ім'я всередині сценарію теж).


Я не можу створити обгортку, довгу історію, ... як я можу виключити поточний PID процесу?
realtebo

@realtebo ummm..виключити значення PID?
heemayl

2

Спробуйте з цим фрагментом:

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

Важливо не писати, pgrep|trтому що це розщелиться до однойменної оболонки.


2

Оберніть команду, яку ви хочете виконати у зграї, щоб переконатися, що одночасно працює лише одна копія. Використовуйте файл lock_file, збережений у дереві домашнього каталогу користувача, щоб кожен користувач мав власний файл блокування. Наприклад:

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

'команда' може бути будь-якою командою bash або сценарієм.

man flock наводить приклади використання


Дійсно, я взагалі не знав flockкоманди
realtebo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.