2009-10-30 13 views
37

Sto cercando di implementare un semplice server di log in Bash. Dovrebbe prendere un file come parametro e servirlo su una porta con netcat.Come ottenere il PID di un processo che viene convogliato su un altro processo in Bash?

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

Ma il problema è che quando il netcat termina, la coda viene lasciata indietro. (Chiarimento: se non eseguo il fork del processo di coda continuerà a funzionare per sempre anche il netcat termina.)

Se in qualche modo conosco il PID della coda, potrei successivamente ucciderlo.
Ovviamente, usando $! restituirà il PID di netcat.

Come posso ottenere il PID del processo di coda?

+0

Cosa succede se non si usa '&'? 'tail -f' dovrebbe solo aspettare lì. Non capisco cosa sia il '&', anche se sembra che faccia parte di uno script più grande. Ad ogni modo, se uccidessi la pipa, penserei che la coda sarebbe poi morta (a patto che non lo sfondo). –

+0

dalla mia comprensione delle shell, e in questo caso è usato con il significato di "start to background". Almeno, questo è il comportamento che sto vedendo proprio ora, cercando di applicare la soluzione al mio problema simile. – starturtle

risposta

27

scrittura di coda nel descrittore di file 3, e quindi acquisire da lì.

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

Ho usato una variante: '(tail -f $ 1 & echo $!> Pid) | nc -l -p 9977' (non sono sicuro del motivo per cui utilizzare il descrittore di file 3 potrebbe essere d'aiuto quando si reindirizza a un file) – Wernight

+0

Non so perché, ma la mia soluzione fallisce dopo che sono state emesse un paio di righe di log. Probabilmente quando il buffer pipe è pieno. Quindi il processo iniziale sembra attendere che la pipa venga elaborata. – Wernight

1

Un modo sarebbe quello di fare semplicemente un -ef ps e grep per la coda con il ppid sceneggiatura

+0

Non riesco a farlo usando il ppid perché è staccato quando lo forzo in una subshell. Ma sono riuscito a farlo usando il parametro args del programma 'ps'. –

6

Forse si potrebbe utilizzare un fifo, in modo da poter catturare il PID del primo processo, ad esempio:

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 
+0

Sì, l'ho provato prima. Il problema dell'utilizzo di fifo è lo stesso: pipe non viene mai terminata, quindi il gatto rimane in esecuzione anche quando netcat termina. Anche il controllo rimane nella linea del gatto, quindi non esegue mai l'uccisione. –

+0

Questo è strano - lo script sopra ha funzionato perfettamente per me su Mac OS X. L'unica differenza era che ho omesso il flag '-p' per nc. –

+0

Forse è un problema di piattaforma (su come gestire i tubi). Ci sto provando su una macchina Linux. grazie comunque per la tua risposta! –

1

hai provato:

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

(non testato)

O -e con un file di script se il tuo nc non ha -c. Potrebbe essere necessario avere un nc che è stato compilato con l'opzione GAPING_SECURITY_HOLE. Sì, dovresti dedurre avvertimenti appropriati dal nome di questa opzione.

+0

Oh, non ci avevo mai pensato. Vorrei che funzionasse :) Ma non si sta ancora concludendo a causa della natura di "tail -f" –

+0

Che dire senza il '-f'? –

+0

senza il -f, è OK. Ma voglio servirlo in tempo reale. –

2

Infine, sono riuscito a trovare il processo di coda utilizzando ps. Grazie all'idea di ennuikiller.

Ho usato il ps per arrendersi dagli args e ucciderlo. È una specie di hack ma ha funzionato. :)

Se riesci a trovare un modo migliore per favore condividi.

Ecco lo script completo:
(ultima versione può essere trovato qui: http://docs.karamatli.com/dotfiles/bin/logserver) PID

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 
+0

Il comando PID del comando tail non dovrebbe essere disponibile in '$!' In modo che tu possa semplicemente fare 'kill $!' Invece di 'kill_tail'? – tripleee

2

ncat termina automaticamente tail -f all'uscita (su 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 
1

È possibile memorizzare il pid del comando tail in una variabile utilizzando reindirizzamenti Bash di I/O solo (vedi How to get the PID of a process in a 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 
28

Un'altra opzione: utilizzare un reindirizzamento a subshell. Questo cambia l'ordine in cui vengono avviati i processi in background, quindi $! dà PID del processo tail.

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

Il vantaggio di questo approccio è che dopo l'attesa, $? detiene anche lo stato di uscita –

+0

Questo sembra l'approccio più pulito da un colpo lungo. Cambia qualcosa (rispetto alle tubazioni standard) dal punto di vista di un processo diverso dall'ordine di partenza? – wlritchi

+8

In realtà, la sintassi su tale reindirizzamento è errata (sebbene il principio sia corretto). '> (foo)' è sostituito dal nome del nuovo descrittore di file, mentre '>> (foo)' in realtà reindirizza l'output ad esso. Vuoi che la prima riga sia 'tail -f $ 1>> (nc -l -p 9977) &'. – wlritchi

9

come su questa:

jobs -x echo %1 

%1 è per primo lavoro in catena, %2 per il secondo, ecc jobs -x sostituisce specificatore lavoro con PID.

+1

Questa deve essere la soluzione più pulita + più corta di "come ottenere il pid di qualsiasi processo in precedenza nella catena" Grazie! Funziona anche con "&" per ottenere il pid di processo in precedenza nella catena in esecuzione in background! Per esempio. 'dd if =/dev/urandom bs = 1M count = 1024 | sha1sum & pid = $ (jobs - x echo% 1) ' ' kill -USR1 $ pid' – JATothrim

0

Non è una risposta ideale, ma ho trovato una soluzione per un demone logger ho lavorato:

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

da $ info coda:

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

Questo funziona per me (SLES Linux):

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

il ps|grep|kill trucco menzionati in questo thread non funzionerebbe se un utente può eseguire lo script per due "istanze" sulla stessa macchina.

jobs -x echo %1 non ha funzionato per me (la pagina man non ha la bandiera -x) ma mi ha dato l'idea di provare jobs -p.

0

L'opzione --pid to tail è il tuo migliore amico qui. Ti permetterà il controllo totale della pipeline in esecuzione in background. leggi le opzioni del comando tail per una maggiore resilienza nel caso in cui il tuo file venga attivamente ruotato da un altro processo che potrebbe lasciarti in coda un inode inattivo. L'esempio seguente, sebbene non utilizzato per elaborare i dati, dimostra la restrizione "imposta" sulla coda e la possibilità di dirgli di uscire in qualsiasi momento. Questo è usato per misurare la pressione di servizio su 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 
Problemi correlati