web-dev-qa-db-fra.com

Comment obtenir le PID d'un processus relié à un autre processus dans Bash?

J'essaie d'implémenter un serveur de journalisation simple dans Bash. Il devrait prendre un fichier en paramètre et le servir sur un port avec netcat.

( tail -f $1 & ) | nc -l -p 9977

Mais le problème est que lorsque le netcat se termine, la queue est laissée derrière. (Précision: si je n'utilise pas le processus final, il continuera à fonctionner indéfiniment, même le netcat se termine.) 

Si je connais le PID de la queue, je pourrais le tuer après.
Évidemment, en utilisant $! renverra le PID de netcat.

Comment puis-je obtenir le PID du processus de queue?

44
Ertuğ Karamatlı

Écrivez le PID de tail dans le descripteur de fichier 3, puis capturez-le à partir de là.

( tail -f $1 & echo $! >&3 ) 3>pid | nc -l -p 9977
kill $(<pid)
34
bobbogo

Une autre option: utiliser une redirection vers un sous-shell. Cela change l'ordre dans lequel les processus en arrière-plan sont démarrés, donc $! donne le PID du processus tail.

tail -f $1 > >(nc -l -p 9977) &
wait $!
34
VladV

que dis-tu de ça:

jobs -x echo %1

%1 est pour le premier travail de la chaîne, %2 pour le second, etc. jobs -x remplace le spécificateur de travail par PID.

11
przemas

Cela fonctionne pour moi (SLES Linux):

tail -F xxxx | tee -a yyyy &
export TAIL_PID=`jobs -p`
# export TEE_PID="$!"

L'astuce ps|grep|kill mentionnée dans ce fil ne fonctionnerait pas si un utilisateur pouvait exécuter le script pour deux "instances" sur le même ordinateur.

jobs -x echo %1 n'a pas fonctionné pour moi (la page de manuel n'ayant pas l'indicateur -x) mais m'a donné l'idée d'essayer jobs -p.

9
James Shau

Vous pourriez peut-être utiliser un fifo pour pouvoir capturer le pid du premier processus, par exemple:

FIFO=my_fifo

rm -f $FIFO
mkfifo $FIFO

tail -f $1 > $FIFO &
TAIL_PID=$!

cat $FIFO | nc -l -p 9977

kill $TAIL_PID

rm -f $FIFO
6
martin clayton

ncat termine automatiquement tail -f à la sortie (sous Mac OS X 10.6.7)!

# simple log server in Bash using ncat
# cf. http://nmap.org/ncat/
touch file.log
ncat -l 9977 -c "tail -f file.log" </dev/null   # terminal window 1
ncat localhost 9977 </dev/null                  # terminal window 2
echo hello > file.log                           # terminal window 3
2
pjil

Enfin, j'ai réussi à trouver le processus final en utilisant ps. Merci à l'idée de l'ennuikiller.

J'ai utilisé le ps pour grep queue des arguments et le tuer. C'est un peu un bidouillage mais ça a fonctionné. :)

Si vous pouvez trouver un meilleur moyen s'il vous plaît partager.

Voici le script complet:
(La dernière version peut être trouvée ici: http://docs.karamatli.com/dotfiles/bin/logserver )

if [ -z "$1" ]; then
    echo Usage: $0 LOGFILE [PORT]
    exit -1
fi
if [ -n "$2" ]; then
    PORT=$2
else
    PORT=9977
fi

TAIL_CMD="tail -f $1"

function kill_tail {
    # find and kill the tail process that is detached from the current process
    TAIL_PID=$(/bin/ps -eo pid,args | grep "$TAIL_CMD" | grep -v grep | awk '{ print $1 }')
    kill $TAIL_PID
}
trap "kill_tail; exit 0" SIGINT SIGTERM

while true; do
    ( $TAIL_CMD & ) | nc -l -p $PORT -vvv
    kill_tail
done
2
Ertuğ Karamatlı

Vous pouvez stocker le pid de la commande tail dans une variable en utilisant uniquement les redirections Bash I/O (voir Comment obtenir le PID d'un processus dans un pipeline ).

# terminal window 1
# using nc on Mac OS X (FreeBSD nc)
: > /tmp/foo
PID=$( { { tail -f /tmp/foo 0<&4 & echo $! >&3 ; } 4<&0 | { nc -l 9977 ;} & } 3>&1 | head -1 )
kill $PID

# terminal window 2
nc localhost 9977

# terminal window 3
echo line > /tmp/foo
1
chad

As-tu essayé:

nc -l -p 9977 -c "tail -f $1"

(non testé)

Ou -e avec un fichier de script si votre nc n'a pas -c. Vous devrez peut-être avoir une nc qui a été compilée avec l'option GAPING_SECURITY_HOLE. Oui, vous devez déduire les mises en garde appropriées à partir de ce nom d'option.

1
Dennis Williamson

Une solution serait simplement de faire un ps -ef et grep for tail avec votre script ppid

1
ennuikiller

Pas une réponse idéale, mais j'ai trouvé une solution de contournement pour un démon enregistreur sur lequel j'ai travaillé:

#!/bin/sh
tail -f /etc/service/rt4/log/main/current --pid=$$ | grep error

à partir de $ info tail:

--pid=PID
          with -f, terminate after process ID, PID dies
0
edibleEnergy

Vous pouvez utiliser la commande coproc deux fois.

L'exemple donné se traduit par:

coproc TAIL { tail -f $1; }; exec {TAIL[1]}<&-
coproc NC { nc -v -l -p 9977; } <&"${TAIL[0]}" >&1
wait $NC_PID; echo "nc exit code: $!"
kill $TAIL_PID; echo "done"

(J'ai jeté un -v et un couple echo pour le dépannage.)

Utiliser coproc ressemble beaucoup à utiliser Popen () dans divers autres langages de script.

0
pipe_of_sharks

L'option --pid to tail est votre meilleur ami ici. Cela vous permettra un contrôle total du pipeline en arrière-plan. lisez les options de la commande tail pour plus de résilience dans le cas où votre fichier est soumis à une rotation active par un autre processus, ce qui pourrait vous laisser terminer un inode inactif. L'exemple ci-dessous, même s'il n'est pas utilisé pour traiter les données, démontre la restriction "imposée" de la queue et la possibilité de lui dire de quitter à tout moment. Ceci est utilisé pour mesurer la pression de service sur httpd.

  # Set the tail to die in 100 second even if we die unexpectedlly.
sleep 100 & ;  ctlpid=$!
tail -q -n 0 --follow=name --retry --max-unchanged-stats=1 --pid=$ctlpid -f  /var/log/httpd/access_log 2>/dev/null | wc –l > /tmp/thisSampleRate &
…. Do some other work
….  Can kill the pipe at any time by killing $ctlpid 
…. Calculate preassure if /tmp/thisSampleRate is ready
0
Danny