Làm thế nào để bạn xác định lệnh thực tế đang dẫn vào bạn?


9

Hãy nói rằng tôi có một kịch bản bash được gọi log.sh. Trong kịch bản này, tôi muốn đọc đầu vào từ một đường ống, nhưng tôi cũng muốn biết lệnh được sử dụng để nhập đường ống vào tôi. Thí dụ:

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

Trong kịch bản shell, tôi muốn biết lệnh tail -f /var/log/httpd/error.


Tôi vô cùng tò mò về lý do tại sao bạn muốn điều này.
Ignacio Vazquez-Abrams

Tôi đoán là bạn đang thực hiện một số loại chương trình GUI có thể chụp và xử lý các pids?
palbakulich

Tôi muốn biết để tôi có thể phân biệt nơi để đặt kết quả. Tùy thuộc vào lệnh và tên tệp, tôi muốn thực hiện các hành động khác nhau.
Thánh John Johnson

4
Nghe có vẻ như một sự vi phạm lớn đối với Nguyên tắc tối thiểu gây ngạc nhiên cho tôi. Nếu tập lệnh nên làm những việc khác nhau trong các trường hợp khác nhau, thì điều đó nên được kiểm soát bởi một tùy chọn dòng lệnh thay vì nơi đầu vào của nó đến từ đâu.
Dave Sherohman

@Dave, tôi đồng ý. Chúng ta hãy nói, vì ví dụ này, tôi chỉ muốn 'biết' lệnh đến là gì.
Thánh John Johnson

Câu trả lời:


7

Akira đề nghị sử dụng lsof.

Đây là cách bạn có thể viết kịch bản:

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

Chạy nó:

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

Một cách khác là sử dụng các nhóm quy trình.

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

Chạy nó:

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

Lưu ý rằng cả hai chỉ hoạt động nếu lệnh ở bên trái của ống chạy đủ lâu psđể nhìn thấy nó. Bạn nói rằng bạn đang sử dụng nó với tail -f, vì vậy tôi nghi ngờ đây là một vấn đề.

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1

thay vì bài đăng lớn này tôi sẽ đưa ra câu trả lời thứ 2 với kịch bản dựa trên lsof. công việc tốt đẹp cho điều đó
akira

@akira Cảm ơn. Phải mất một vài nỗ lực để làm cho nó sạch sẽ và di động. Đã học được một vài điều về Procfs và lsof trên đường đi. Cảm ơn ý tưởng.
Mikel

Tôi chấp nhận của bạn vì nó đưa ra câu trả lời mà người khác có thể trực tiếp sử dụng. @Akira, bạn đã làm hầu hết công việc, xin lỗi tôi cũng không thể chấp nhận bạn.
Thánh John Johnson

10

đường ống sẽ xuất hiện dưới dạng một mục trong danh sách các trình biên dịch mở của quy trình của bạn:

 % 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

bạn cũng có thể sử dụng một cái gì đó như:

 % 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

vì vậy, hơn là bạn có nút của đường ống :) bây giờ bạn có thể tìm kiếm mọi quy trình khác theo /proc/đường ống đó. sau đó bạn sẽ có lệnh đang chuyển cho bạn:

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

trong ví dụ này, catđường ống đến phường sh. trong /proc/29889bạn có thể tìm thấy một tệp được gọi là cmdlinecho bạn biết, chính xác được gọi là gì:

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

các trường của dòng lệnh được phân tách bằng NUL, do đó trông hơi xấu xí :)


Tôi không biết nên chấp nhận câu trả lời nào. @Akira, bạn đã phân tích thực tế về cách xác định nó, trong khi @Mikel đưa cho tôi một kịch bản. Cách để làm cho mọi thứ tuyệt vời + khó khăn.
Thánh John Johnson

1

Đây là một giải pháp nhỏ gọn sử dụng hiện đại lsoftrên các bản phân phối Linux hiện đại:

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

Điều này liệt kê các tệp điểm cuối ( +E) của FD 0 trên tiến trình shell hiện tại ( -p $$ -a -d 0), sau đó giới hạn đầu ra chỉ ở mức PID ( -t), mang lại các PID trên cả hai mặt của đường ống.

Lưu ý rằng:

  1. Có thể có nhiều hơn một PID được tìm thấy ở đầu nguồn, ví dụ như { echo Hi; sleep 5 ; } | whatpipe.shsẽ có khả năng mang lại một bash(lớp con đầu vào) và sleep 5.
  2. +Echỉ có sẵn nếu lsofđược biên dịch với -DHASUXSOCKEPT. Điều đó đúng với hầu hết các bản phân phối Linux hiện đại, nhưng hãy kiểm tra cài đặt của bạn bằng:lsof -v 2>&1 | grep HASUXSOCKEPT
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.