Detectar se uma ferramenta já está em execução, mas SOMENTE para usuário atual

8

Até agora eu usei

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

Para detectar o pid de uma instância eventualmente em execução da minha ferramenta.

Esta é a versão curta do arquivo my-tool, um script bash executável

#!/bin/bash 

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

 ... if not running go on 

Mas agora preciso permitir uma única instância por usuário e várias por máquina , para que possamos ter até 100 my-tool em execução no mesmo momento, mas apenas 1 por usuário.

Note que preciso de um teste para criar algo como um singleton. Se a ferramenta for iniciada e houver outra instância em execução, ela será fechada.

Resumindo: eu preciso que um script bash possa detectar se ele mesmo já está sendo executado para o usuário atual e, nesse caso, ele deve sair.

Como?

    
por realtebo 26.07.2016 / 14:04

4 respostas

12

Use pgrep :

pgrep -cxu $USER -f my-tool

As opções usadas são:

   -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 você quiser usar isso em um script bash que verifica se ele já está em execução, você pode usar $0 . Isso se expande para o caminho do script atual (por exemplo, /home/username/bin/foo.sh ), mas precisamos apenas de foo.sh . Para conseguir isso, podemos remover tudo até as últimas / usando as ferramentas de manipulação de string do bash : ${0##*/} . Isso significa que podemos fazer 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

Você também pode considerar o uso de arquivos de bloqueio para isso:

## 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
    
por terdon 26.07.2016 / 14:07
7

Você pode usar pgrep para descobrir se um processo está sendo executado por um usuário específico e, em seguida, iniciar o processo, se ainda não estiver sendo executado pelo usuário:

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

A variável de ambiente, $USER , será expandida para o usuário conectado no momento, ou seja, o usuário que está executando o script. Como estamos interessados apenas em saber se o my-tool está em execução ou não, apenas usar o status de saída diretamente com if construct é suficiente.

Use este script como um wrapper para iniciar my-tool e fazer os usuários usarem apenas isso ou renomeá-lo como my-tool e renomear o original my-tool para outra coisa (e alterar o nome dentro do script também). / p>     

por heemayl 26.07.2016 / 14:18
2

Experimente com este snippet:

#!/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 não escrever pgrep|tr , porque isso se encaixaria no mesmo shell.

    
por rexkogitans 26.07.2016 / 16:37
2

Enrole o comando que você deseja executar em um lote para garantir que apenas uma cópia seja executada por vez. Use um lock_file armazenado na árvore de diretórios home de um usuário para que cada usuário tenha seu próprio arquivo de bloqueio. Por exemplo:

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

'comando' pode ser qualquer comando bash ou script.

man flock fornece exemplos de uso

    
por a guest 26.07.2016 / 17:48

Tags