Come si determina il comando effettivo che ti sta eseguendo il piping?


9

Diciamo che ho uno script bash chiamato log.sh. In questo script, voglio leggere l'input da una pipe, ma voglio anche conoscere il comando usato per pipe l'input in me. Esempio:

tail -f /var/log/httpd/error | log.sh

Nello script della shell, voglio conoscere il comando tail -f /var/log/httpd/error.


Sono estremamente curioso di sapere perché lo desideri.
Ignacio Vazquez-Abrams,

Suppongo che stai realizzando un qualche tipo di programma di interfaccia grafica che acquisisce ed elabora pids?
Palbakulich,

Vorrei sapere in modo da poter distinguere dove mettere i risultati. A seconda del comando e del nome file, vorrei eseguire diverse azioni.
St. John Johnson,

4
Mi sembra una grave violazione del Principio della minima sorpresa. Se lo script dovrebbe fare cose diverse in circostanze diverse, allora dovrebbe essere controllato da un'opzione della riga di comando piuttosto che da dove proviene il suo input.
Dave Sherohman,

@Dave, sono d'accordo. Diciamo solo, per il bene di questo esempio, voglio solo "sapere" qual è il comando in arrivo.
St. John Johnson,

Risposte:


7

Akira ha suggerito di utilizzare lsof.

Ecco come potresti copiarlo:

whatpipe2.sh

#!/bin/bash

pid=$$
pgid=$(ps -o pgid= -p $pid)
lsofout=$(lsof -g $pgid)
pipenode=$(echo "$lsofout" | awk '$5 == "0r" { print $9 }')
otherpids=$(echo "$lsofout" | awk '$5 == "1w" { print $2 }')
for pid in $otherpids; do
    if cmd=$(ps -o cmd= -p $pid 2>/dev/null); then
        echo "$cmd"
        break
    fi
done

Eseguendolo:

$ tail -f /var/log/messages | ./whatpipe2.sh
tail -f /var/log/messages
^C

Un altro modo è utilizzare i gruppi di processi.

whatpipe1.sh

#!/bin/bash    

pid=$$
# ps output is nasty, can (and usually does) start with spaces
# to handle this, I don't quote the "if test $_pgrp = $pgrp" line below
pgrp=$(ps -o pgrp= -p $pid)
psout=$(ps -o pgrp= -o pid= -o cmd=)
echo "$psout" | while read _pgrp _pid _cmd; do
    if test $_pgrp = $pgrp; then
        if test $_pid != $pid; then
            case $_cmd in
            ps*)
                # don't print the "ps" we ran to get this info
                # XXX but this actually means we exclude any "ps" command :-(
                ;;
            *)
                echo "$_cmd"
                ;;
            esac
        fi
    fi
done

Eseguendolo:

$ tail -f /var/log/messages | ./whatpipe1.sh
tail -f /var/log/messages
^C

Notare che funzionano entrambi solo se il comando sul lato sinistro del tubo viene eseguito abbastanza a lungo da pspoterlo vedere. Hai detto che lo stavi usando tail -f, quindi dubito che questo sia un problema.

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1

invece di questo enorme post avrei dato una seconda risposta con lo script basato su lsof. bel lavoro per quello.
Akira,

@akira Grazie. Ci sono voluti alcuni tentativi per renderlo pulito e portatile. Lungo la strada ho imparato alcune cose su procfs e lsof. Grazie per l'idea
Mikel,

Ho accettato la tua in quanto fornisce una risposta che altre persone possono usare direttamente. @Akira, hai fatto la maggior parte del lavoro, mi spiace non poter accettare anche il tuo.
St. John Johnson,

10

la pipe apparirà come una voce nell'elenco dei descrittori di file aperti del processo:

 % ls -l /proc/PID/fd
 lr-x------ 1 xyz xyz 64 Feb 11 08:05 0 -> pipe:[124149866]
 lrwx------ 1 xyz xyz 64 Feb 11 08:05 1 -> /dev/pts/2
 lrwx------ 1 xyz xyz 64 Feb 11 08:05 2 -> /dev/pts/2
 lr-x------ 1 xyz xyz 64 Feb 11 08:05 10 -> /tmp/foo.sh

potresti anche usare qualcosa come:

 % lsof -p PID
 sh      29890 xyz  cwd    DIR   0,44    4096  77712070 /tmp
 sh      29890 xyz  rtd    DIR   0,44    4096  74368803 /
 sh      29890 xyz  txt    REG   0,44   83888  77597729 /bin/dash
 sh      29890 xyz  mem    REG   0,44 1405508  79888619 /lib/tls/i686/cmov/libc-2.11.1.so
 sh      29890 xyz  mem    REG   0,44  113964  79874782 /lib/ld-2.11.1.so
 sh      29890 xyz    0r  FIFO    0,6         124149866 pipe
 sh      29890 xyz    1u   CHR  136,2                 4 /dev/pts/2
 sh      29890 xyz    2u   CHR  136,2                 4 /dev/pts/2
 sh      29890 xyz   10r   REG   0,44      66  77712115 /tmp/foo.sh

quindi, di quanto tu abbia l'inode della pipe :) ora puoi cercare ogni altro processo /proc/per quella pipe. allora avrai il comando che ti sta eseguendo il piping:

 % lsof | grep 124149866 
 cat     29889 xyz    1w  FIFO                0,6          124149866 pipe
 sh      29890 xyz    0r  FIFO                0,6          124149866 pipe

in questo esempio, catconvogliato verso i reparti sh. in /proc/29889puoi trovare un file chiamato cmdlineche ti dice cosa si chiamava esattamente:

 % cat /proc/29889/cmdline
 cat/dev/zero%  

i campi della riga di comando sono separati da NUL, quindi sembra un po 'brutto :)


Non so quale risposta accettare. @Akira, hai dato la scomposizione effettiva su come determinarlo, mentre @Mikel mi ha dato una sceneggiatura. Modo per rendere le cose fantastiche + difficili.
St. John Johnson,

1

Ecco una soluzione compatta che utilizza moderne lsofdistribuzioni Linux moderne:

cmd=$(lsof -t -p $$ -a -d 0 +E | while read p; do
    [ $p -ne $$ ] && echo "$(tr \\000 " " </proc/$p/cmdline)"
done)

Questo elenca i file endpoint ( +E) di FD 0 sul processo shell corrente ( -p $$ -a -d 0), quindi limita l'output ai soli PID ( -t), producendo i PID su entrambi i lati del tubo.

Nota che:

  1. È possibile che sia stato trovato più di un PID sull'estremità di origine, ad esempio { echo Hi; sleep 5 ; } | whatpipe.shprobabilmente produrrà un bash(sottoshell di input) e sleep 5.
  2. +Eè disponibile solo se è lsofstato compilato -DHASUXSOCKEPT. Questo dovrebbe essere vero per la maggior parte delle distro Linux moderne, ma controlla comunque l'installazione con:lsof -v 2>&1 | grep HASUXSOCKEPT
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.