Определить, если инструмент уже запущен, но ТОЛЬКО для текущего пользователя

8

До сих пор я использовал

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

Чтобы обнаружить pid запущенного экземпляра my-tool.

Это короткая версия файла 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 my-tool, но только 1 на пользователя.

Обратите внимание, что мне нужен тест для создания чего-то вроде синглтона. Если инструмент будет запущен и запущен другой экземпляр, он закроется сам.

Вкратце: мне нужно, чтобы bash-скрипт мог определить, работает ли сам по себе текущий пользователь, и в этом случае он должен завершиться.

Как ?

realtebo
источник
Вы не должны использовать 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
terdon
источник
1
Вместо того, чтобы touch /tmp/$USER.foo.lockиспользовать PID процесса, echo $$ >/tmp/$USER.foo.lockможно было бы включить логику, чтобы проверить, существует ли такой процесс на самом деле.
Монти Хардер
@MontyHarder действительно, очень хорошее предложение. Ответ отредактирован, спасибо.
тердон
@terdon, имел проблемы с мобильным телефоном, удалил этот комментарий в следующую секунду. Я имел ввиду $(< pidfile)вместо $(cat pidfile).
sdkks
7

Вы можете использовать pgrepкоманду 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работает ли или нет, достаточно просто использовать состояние выхода напрямую с помощью ifconstruct.

Используйте этот скрипт в качестве оболочки для запуска my-toolи заставьте пользователей использовать это только или переименуйте его как my-toolи переименуйте оригинал my-toolво что-то другое (и измените имя внутри скрипта тоже).

heemayl
источник
Я не могу сделать оболочку, длинную историю, ... как я могу исключить текущий PID процесса?
Realtebo
@realtebo мммм ... исключить значение 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потому что это превратилось бы в одноименную оболочку.

rexkogitans
источник
2

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

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

'command' может быть любой командой или скриптом bash.

man flock приводит примеры использования

Гость
источник
На самом деле, я вообще не знал flockкоманды
realtebo