Jak określasz rzeczywiste polecenie, które do ciebie dołącza?

9

Powiedzmy, że mam skrypt bash o nazwie log.sh. W tym skrypcie chcę odczytać dane wejściowe z potoku, ale chcę również poznać polecenie użyte do przesłania danych wejściowych do mnie. Przykład:

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

W skrypcie powłoki chcę znać polecenie tail -f /var/log/httpd/error.

St. John Johnson
źródło
Jestem bardzo ciekawy, dlaczego tego chcesz.
Ignacio Vazquez-Abrams
Sądzę, że tworzysz jakiś program GUI, który przechwytuje i przetwarza stawki?
palbakulich
Chciałbym wiedzieć, dzięki czemu mogę rozróżnić, gdzie umieścić wyniki. W zależności od polecenia i nazwy pliku chciałbym wykonać różne czynności.
St. John Johnson
4
Dla mnie to brzmi jak poważne naruszenie zasady najmniejszej niespodzianki. Jeśli skrypt powinien robić różne rzeczy w różnych okolicznościach, powinno to być kontrolowane przez opcję wiersza poleceń, a nie przez to, skąd pochodzi jego wejście.
Dave Sherohman,
@Dave, zgadzam się. Powiedzmy, że na potrzeby tego przykładu chcę po prostu „wiedzieć”, jakie jest nadchodzące polecenie.
St. John Johnson

Odpowiedzi:

7

Akira zasugerował użycie lsof.

Oto jak możesz to napisać:

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

Uruchamianie:

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

Innym sposobem jest użycie grup procesów.

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

Uruchamianie:

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

Zauważ, że oba działają tylko wtedy, gdy polecenie po lewej stronie potoku działa wystarczająco długo, psaby je zobaczyć. Powiedziałeś, że go używasz tail -f, więc wątpię, żeby to był problem.

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1
Mikel
źródło
zamiast tego ogromnego posta dałbym drugą odpowiedź za pomocą skryptu opartego na lsof. niezła robota dla tego.
akira
@akira Thanks. Podjęto kilka prób, aby uczynić go czystym i przenośnym. Po drodze dowiedziałem się kilku rzeczy o procfs i lsof. Dzięki za pomysł.
Mikel
Zaakceptowałem twoją, ponieważ daje odpowiedź, z której inni ludzie mogą bezpośrednio skorzystać. @Akira, wykonałeś większość pracy, przepraszam, że nie mogłem zaakceptować twojej.
St. John Johnson
10

potok pojawi się jako wpis na liście otwartych deskryptorów plików twojego procesu:

 % 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

możesz także użyć czegoś takiego:

 % 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

więc, niż masz i-węzeł potoku :) możesz teraz przeszukiwać każdy inny proces /proc/dla tego potoku. wtedy będziesz miał polecenie, które do ciebie dołącza:

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

w tym przykładzie catpoprowadzono do totemów sh. w /proc/29889możesz znaleźć plik o nazwie, cmdlinektóry mówi ci, co dokładnie nazywało się:

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

pola wiersza poleceń są oddzielone przez NUL, więc wygląda trochę brzydko :)

akira
źródło
Nie wiem, którą odpowiedź zaakceptować. @Akira, podałeś aktualny podział, jak to ustalić, a @Mikel dał mi skrypt. Sposób, aby wszystko było niesamowite + trudne.
St. John Johnson
1

Oto kompaktowe rozwiązanie wykorzystujące nowoczesne i lsofnowoczesne dystrybucje Linuksa:

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

Zawiera listę plików końcowych ( +E) FD 0 w bieżącym procesie powłoki ( -p $$ -a -d 0), a następnie ogranicza dane wyjściowe tylko do PID ( -t), uzyskując PID po obu stronach potoku.

Uwaga:

  1. Na końcu źródła może znajdować się więcej niż jeden PID, np. { echo Hi; sleep 5 ; } | whatpipe.shPrawdopodobnie da bash(wejściową podpowłokę) i sleep 5.
  2. +Ejest dostępny tylko, jeśli lsofzostał skompilowany z -DHASUXSOCKEPT. To powinno być prawdą w przypadku większości współczesnych dystrybucji Linuksa, ale mimo to sprawdź swoją instalację za pomocą:lsof -v 2>&1 | grep HASUXSOCKEPT
Adrian
źródło