¿Cómo obtener el PID de un proceso que se canaliza a otro proceso en Bash?
Pregunta
Estoy intentando implementar un servidor de registro simple en Bash. Debe tomar un archivo como parámetro y servirlo en un puerto con netcat.
( tail -f $1 & ) | nc -l -p 9977
Pero el problema es que cuando el netcat termina, la cola se queda atrás. (Aclaración: si no bifurco el proceso final, continuará ejecutándose para siempre, incluso el netcat termina).
Si de alguna manera conozco el PID de la cola, podría matarlo después.
Obviamente, usando $! devolverá el PID de netcat.
¿Cómo puedo obtener el PID del proceso final?
Solución
Escriba el PID de la cola en el descriptor de archivo 3 y luego captúrelo desde allí.
( tail -f $1 & echo $! >&3 ) 3>pid | nc -l -p 9977
kill $(<pid)
Otros consejos
Otra opción: usar una redirección a subshell. Esto cambia el orden en que se inician los procesos en segundo plano, por lo que $! da PID del proceso tail
.
tail -f $1 > >(nc -l -p 9977) &
wait $!
¿qué tal esto?
jobs -x echo %1
% 1
es para el primer trabajo en la cadena, % 2
para el segundo, etc. jobs -x
reemplaza el especificador de trabajo con PID.
Esto funciona para mí (SLES Linux):
tail -F xxxx | tee -a yyyy &
export TAIL_PID=`jobs -p`
# export TEE_PID="$!"
El truco ps | grep | kill
mencionado en este hilo no funcionaría si un usuario puede ejecutar el script para dos " instancias " en la misma máquina.
jobs -x echo% 1
no funcionó para mí (la página de manual no tiene el indicador -x
) pero me dio la idea de probar los trabajos - p
.
Tal vez podría usar un fifo , para que pueda capturar el pid del primer proceso , por ejemplo:
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
Finalmente, he logrado encontrar el proceso de cola usando ps
. Gracias a la idea de ennuikiller.
He usado el ps
para arrancar la cola de los args y matarlo. Es una especie de truco, pero funcionó. :)
Si puede encontrar una mejor manera, comparta.
Aquí está el script completo:
(La última versión se puede encontrar aquí: http://docs.karamatli.com/dotfiles/bin/ servidor de registro )
if [ -z "$1" ]; then
echo Usage: <*> 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
ncat
termina automáticamente tail -f
al salir (en 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
Una forma sería simplemente hacer un ps -ef y grep for tail con su script ppid
¿Has probado:
nc -l -p 9977 -c "tail -f $1"
(no probado)
O -e
con un archivo de script si su nc
no tiene -c
. Es posible que deba tener un nc
que se compiló con la opción GAPING_SECURITY_HOLE
. Sí, debe inferir las advertencias apropiadas del nombre de esa opción.
Puede almacenar el pid del comando tail
en una variable utilizando solo redirecciones de E / S Bash (consulte Cómo obtener el PID de un proceso en una tubería ).
# 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
No es una respuesta ideal, pero encontré una solución para un demonio de registrador en el que trabajé:
#!/bin/sh
tail -f /etc/service/rt4/log/main/current --pid=$ | grep error
desde $ info tail:
--pid=PID
with -f, terminate after process ID, PID dies
La opción --pid para seguir es tu mejor amigo aquí. Le permitirá un control total de la tubería que se ejecuta en segundo plano. lea las opciones del comando de cola para obtener más resistencia en caso de que su archivo sea rotado activamente por otro proceso que podría dejarlo siguiendo un inodo inactivo. El siguiente ejemplo, aunque no se utiliza para procesar los datos, demuestra la "impuesta" restricción en la cola y la capacidad de decirle que salga en cualquier momento. Esto se utiliza para medir la presión del servicio en 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
Puede usar el comando coproc
dos veces.
El ejemplo dado se traduce en:
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"
(He arrojado un -v
y un par de echo
para resolver problemas)
Usar coproc
se parece mucho a usar Popen () en varios otros lenguajes de script.