Bagaimana Anda menentukan perintah aktual yang disalurkan ke Anda?

9

Katakanlah saya memiliki skrip bash yang dipanggil log.sh. Dalam skrip ini, saya ingin membaca input dari sebuah pipa, tetapi saya juga ingin tahu perintah yang digunakan untuk memasukkan pipa ke saya. Contoh:

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

Dalam skrip shell, saya ingin tahu perintahnya tail -f /var/log/httpd/error.

St John Johnson
sumber
Saya sangat ingin tahu mengapa Anda menginginkan ini.
Ignacio Vazquez-Abrams
Dugaan saya, apakah Anda membuat beberapa jenis program GUI yang menangkap & memproses tawaran?
palbakulich
Saya ingin tahu sehingga saya bisa membedakan di mana harus meletakkan hasilnya. Bergantung pada perintah dan nama file, saya ingin melakukan tindakan yang berbeda.
St. John Johnson
4
Itu terdengar seperti pelanggaran besar terhadap Prinsip Kejutan Paling Sedikit. Jika skrip harus melakukan hal-hal yang berbeda dalam keadaan yang berbeda, maka itu harus dikontrol oleh opsi baris perintah daripada dari mana inputnya berasal.
Dave Sherohman
@Dave, saya setuju. Katakan saja, untuk contoh ini, saya hanya ingin 'tahu' apa perintah yang masuk.
St. John Johnson

Jawaban:

7

Akira menyarankan untuk menggunakan lsof.

Begini caranya Anda bisa membuat skrip:

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

Menjalankannya:

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

Cara lain adalah menggunakan grup proses.

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

Menjalankannya:

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

Perhatikan bahwa keduanya hanya berfungsi jika perintah di sisi kiri pipa berjalan cukup lama untuk psmelihatnya. Anda bilang Anda menggunakannya tail -f, jadi saya ragu ini masalah.

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1
Mikel
sumber
bukannya posting besar ini saya akan memberikan jawaban ke-2 dengan skrip berbasis lsof. kerja bagus untuk yang satu itu.
akira
@ Akira Terima kasih. Butuh beberapa upaya untuk membuatnya bersih dan portabel. Mempelajari beberapa hal tentang proses pengadaan dan pengadaan di sepanjang jalan. Terima kasih untuk idenya.
Mikel
Saya menerima Anda karena memberikan jawaban yang dapat digunakan orang lain secara langsung. @ Akira, kamu melakukan sebagian besar pekerjaan, maaf aku tidak bisa menerima milikmu juga.
St. John Johnson
10

pipa akan muncul sebagai entri dalam daftar arsip terbuka dari proses Anda:

 % 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

Anda juga dapat menggunakan sesuatu seperti:

 % 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

jadi, daripada Anda memiliki inode dari pipa :) Anda sekarang dapat mencari setiap proses lainnya di bawah /proc/untuk pipa itu. maka Anda akan memiliki perintah yang mengirimkan kepada Anda:

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

dalam contoh ini, catdisalurkan ke bangsal sh. di /proc/29889Anda dapat menemukan file bernama cmdlineyang memberitahu Anda, apa yang disebut:

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

bidang-bidang pada baris perintah dipisahkan oleh NUL, sehingga terlihat agak jelek :)

akira
sumber
Saya tidak tahu jawaban mana yang harus diterima. @ Akira, Anda memberi rincian aktual tentang cara menentukannya, sementara @Mikel memberi saya skrip. Cara untuk membuat semuanya menjadi luar biasa + sulit.
St. John Johnson
1

Inilah solusi ringkas menggunakan lsofdistribusi modern di Linux modern:

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

Ini mencantumkan file titik akhir ( +E) FD 0 pada proses shell saat ini ( -p $$ -a -d 0), kemudian membatasi output hanya untuk PID ( -t), menghasilkan PID di kedua sisi pipa.

Perhatikan bahwa:

  1. Mungkin ada lebih dari satu PID yang ditemukan di ujung sumber, mis. { echo Hi; sleep 5 ; } | whatpipe.shKemungkinan akan menghasilkan a bash(subkulit input) dan sleep 5.
  2. +Ehanya tersedia jika lsofdikompilasi dengan -DHASUXSOCKEPT. Itu seharusnya benar untuk sebagian besar distro Linux modern, tetapi tetap periksa instalasi Anda dengan:lsof -v 2>&1 | grep HASUXSOCKEPT
Adrian
sumber