Como se livrar do processo recém-iniciado

71

Eu quero iniciar o processo (por exemplo, myCommand) e obter o seu pid (para permitir matá-lo mais tarde).

Eu tentei ps e filtrar por nome, mas não consigo distinguir processo por nomes

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

Porque nomes de processos não são exclusivos.

Eu posso executar o processo por:

myCommand &

Descobri que posso obter esse PID:

echo $!

Existe alguma solução mais simples?

Eu ficaria feliz em executar o myCommand e obter seu PID como resultado de um comando de linha.

rafalmag
fonte

Respostas:

77

O que pode ser mais simples do que echo $!? Como uma linha:

myCommand & echo $!
Jacek Konieczny
fonte
Obrigado mesclar esses comandos com "&" me ajudou muito.
Rafalmag 27/11/2010
8
em um script bash, em um loop que inicia programas, $! não é preciso. Às vezes, retorna pid do próprio script, outras vezes grep ou awk, executam do script qualquer solução para obter especificamente o pid do processo recém-lançado nesse cenário? algo como = pid myprogramteria sido incríveis
2
Note que isso exige que você inicie o comando usando a &como linha anterior; caso contrário, o eco retornará basicamente em branco.
Rogerdpack
2
atribuir a variável como command & echo $!congela a execução nesta etapa :(
Shashank Vivek 15/16/16
26

Embrulhe o comando em um pequeno script

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517 suporta GoFundMonica
fonte
20

Você pode usar sh -ce execobter o PID do comando antes mesmo de ele ser executado.

Para começar myCommand, para que seu PID seja impresso antes de começar a ser executado, você pode usar:

sh -c 'echo $$; exec myCommand'

Como funciona:

Isso inicia um novo shell, imprime o PID desse shell e, em seguida, usa o execbuilt-in para substituir o shell pelo seu comando, garantindo que ele tenha o mesmo PID. Quando seu shell executa um comando com o execbuilt-in, seu shell está realmente se tornando esse comando , em vez do comportamento mais comum de criar uma nova cópia de si mesmo, que possui seu próprio PID separado e que se torna o comando.

Acho que isso é muito mais simples do que as alternativas que envolvem execução assíncrona (com &), controle de tarefas ou pesquisa com ps. Essas abordagens são boas, mas, a menos que você tenha um motivo específico para usá-las - por exemplo, talvez o comando já esteja em execução; nesse caso, procurar por seu PID ou usar o controle de tarefas faria sentido - sugiro considerar isso primeiro. (E eu certamente não consideraria escrever um script complexo ou outro programa para conseguir isso).

Esta resposta inclui um exemplo dessa técnica.


Partes desse comando podem ocasionalmente ser omitidas, mas não geralmente.

Mesmo que o shell que você está usando seja do estilo Bourne e, portanto, ofereça suporte ao execincorporado com essas semânticas, geralmente você não deve evitar o uso sh -c(ou equivalente) para criar um novo processo de shell separado para esse fim, porque:

  • Depois que o shell se torna myCommand, não há shell esperando para executar comandos subseqüentes. sh -c 'echo $$; exec myCommand; foonão seria capaz de tentar executar foodepois de se substituir por myCommand. A menos que você esteja escrevendo um script que execute isso como seu último comando, você não pode simplesmente usar echo $$; exec myCommandem um shell em que esteja executando outros comandos.
  • Você não pode usar um subshell para isso. (echo $$; exec myCommand)pode ser sintaticamente melhor do que sh -c 'echo $$; exec myCommand', mas quando você executa o $$interior ( ), ele fornece o PID do shell pai, não do subshell em si. Mas é o PID do subshell que será o PID do novo comando. Alguns shells fornecem seus próprios mecanismos não portáteis para localizar o PID do subshell, que você pode usar para isso. Em particular, no Bash 4 , (echo $BASHPID; exec myCommand)funciona.

Por fim, observe que alguns shells executam uma otimização onde executam um comando como se fossem exec(ou seja, renunciam primeiro à bifurcação) quando se sabe que o shell não precisará fazer nada posteriormente. Alguns shells tentam fazer isso a qualquer momento, sendo o último comando a ser executado, enquanto outros o fazem apenas quando não há outros comandos antes ou depois do comando, e outros não o fazem. O efeito é que, se você esquecer de escrever exece usar sh -c 'echo $$; myCommand', ele às vezes fornecerá o PID correto em alguns sistemas com alguns shells. Eu recomendo que nunca confie em tal comportamento e, em vez disso, sempre inclua execquando é isso que você precisa.

Eliah Kagan
fonte
Antes de poder executar myCommand, preciso definir várias variáveis ​​de ambiente no meu script bash. Elas serão transferidas para o ambiente em que o execcomando está sendo executado?
User5359531 11/1118
Parece que meu ambiente é transferido para o execcomando. No entanto, essa abordagem não funciona quando myCommandinicia outros processos, com os quais você precisa trabalhar; quando emito um kill -INT <pid>onde pidfoi obtido dessa maneira, o sinal não alcança os subprocessos iniciados por myCommand, enquanto que se eu executar myCommand na sessão atual e Ctrl + C, os sinais se propagam corretamente.
User5359531 11/1118
11
Eu tentei isso, mas o pid do processo myCommand parece ser o resultado do pid pelo eco $$ +1. Estou fazendo algo errado?
Crobar
Meu comando fica assim:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar 28/08
7

Não conheço nenhuma solução mais simples, mas não estou usando $! bom o bastante? Você sempre pode atribuir o valor a alguma outra variável, se precisar mais tarde, como dito por outras pessoas.

Como uma observação lateral, em vez de usar o ps do ps, você pode usar pgrepor pidof.

Carlpett
fonte
5

use exec de um script bash depois de registrar o pid em um arquivo:

exemplo:

suponha que você tenha um script chamado "forever.sh" que deseja executar com args p1, p2, p3

código-fonte forever.sh:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

crie um reaper.sh:

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

execute forever.sh através de reaper.sh:

./reaper.sh ./forever.sh p1 p2 p3 p4 &

forever.sh não faz nada além de registrar uma linha no syslog a cada 5 segundos

agora você tem o pid em /var/run/forever.sh.pid

cat /var/run/forever.sh.pid 
5780

e forever.sh está funcionando bem. syslog grep:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

você pode vê-lo na tabela de processos:

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4
user237419
fonte
3
ah, e o "oneliner": / bin / sh -c 'echo $$> / tmp / my.pid && exec program args' &
user237419
11
Para preservar adequadamente o espaço em branco interno nos argumentos, você deve usar em exec "$@"vez de exec $*. Tecnicamente, o que você precisa preservar não é o espaço em branco, mas as ocorrências dos caracteres no parâmetro do shell do IFS (o padrão é espaço, tabulação e nova linha).
precisa
ponto tomado. :)
user237419
Obrigado, eu não sabia $$ parâmetro. Pode ser muito útil.
Rafalmag
3

No shell bash, uma alternativa ao $!pode ser o jobs -pbuilt-in. Em alguns casos, o !in $!é interpretado pelo shell antes (ou em vez de) da expansão da variável, levando a resultados inesperados.

Isso, por exemplo, não funcionará:

((yourcommand) & echo $! >/var/run/pidfile)

enquanto isso irá:

((yourcommand) & jobs -p >/var/run/pidfile)
mustaccio
fonte
Eu acho que você quis dizer ((seu comando) & jobs -p> / var / run / pidfile).
Dom
1

Você pode usar algo como:

$ myCommand ; pid=$!

Ou

$ myCommand && pid=$!

Os dois comandos podem ser articulações usando ;ou &&. No segundo caso, o pid será definido apenas se o primeiro comando for bem-sucedido. Você pode obter o ID do processo $pid.

Khaled
fonte
3
O OP quer obter o PID para que ele possa matá-lo mais tarde. ; e && exigem que o processo original saia antes do eco $! É executado.
user9517 suporta GoFundMonica
Sim você está certo. Isso lhe dará o pid após o encerramento do myCommand.
Khaled
6
A referência $!após &&ou ;nunca fornecerá o PID do processo iniciado para o lado esquerdo do separador de comandos. $!é definido apenas para processos iniciados de forma assíncrona (por exemplo, geralmente com &mas alguns shells também possuem outros métodos).
precisa
é útil, e por que ninguém votou acima de mim? embora bom trabalho :)
temple
1

Esta é uma resposta um tanto quanto improvável e provavelmente não funcionará para a maioria das pessoas. Também é um risco enorme para a segurança, então não faça isso, a menos que você tenha certeza de que estará seguro e que as entradas são higienizadas e ... bem, você entendeu.

Compile o pequeno programa C aqui em um binário chamado start(ou o que você quiser) e execute o programa como./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

Para encurtar a história, isso imprimirá o PID e stdout, em seguida, carregará seu programa no processo. Ainda deve ter o mesmo PID.

tonysdg
fonte
Como posso gravar o número PID retornado por este código em uma variável bash? Por uma razão que não está clara para mim, stdoutnão parece estar registrada neste exemplo:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9
@ Tfb9: Eu honestamente recomendaria uma das outras abordagens; Eu escrevi isso há mais de 2 anos e é de longe um dos métodos mais invasivos / propensos a erros apresentados (ou pelo menos eu acho).
tonysdg
Obrigado pela observação. Eu acho que resolveu o meu problema com estesleep 10 & PID_IS=$!; echo $PID_IS
Tfb9