คุณจะกำหนดคำสั่งที่เกิดขึ้นจริงที่ส่งเข้าไปในตัวคุณได้อย่างไร?


9

log.shสมมติว่าผมมีสคริปต์ทุบตีที่เรียกว่า ในสคริปต์นี้ฉันต้องการอ่านอินพุตจากไพพ์ แต่ฉันต้องการทราบคำสั่งที่ใช้เพื่อไพพ์อินพุตให้ฉันด้วย ตัวอย่าง:

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

ในสคริปต์เปลือก, tail -f /var/log/httpd/errorฉันต้องการทราบคำสั่ง


ฉันสงสัยอย่างมากว่าทำไมคุณถึงต้องการสิ่งนี้
Ignacio Vazquez-Abrams

ฉันเดาว่าคุณกำลังสร้างโปรแกรม GUI บางประเภทที่จับและประมวลผล pids หรือไม่?
palbakulich

ฉันอยากรู้ว่าฉันสามารถแยกแยะผลลัพธ์ได้ที่ไหน ขึ้นอยู่กับคำสั่งและชื่อไฟล์ฉันต้องการดำเนินการต่าง ๆ
เซนต์จอห์นจอห์นสัน

4
ฟังดูเหมือนเป็นการละเมิดหลักการของ Least Surprise สำหรับฉัน หากสคริปต์ควรทำสิ่งต่าง ๆ ภายใต้สถานการณ์ที่แตกต่างกันดังนั้นควรมีการควบคุมโดยตัวเลือกบรรทัดคำสั่งแทนที่จะเป็นที่มาของอินพุต
Dave Sherohman

@ เดฟฉันเห็นด้วย สมมุติว่าเพื่อตัวอย่างนี้ฉันแค่อยากรู้ว่าคำสั่งที่เข้ามาคืออะไร
St. John Johnson

คำตอบ:


7

lsofอากิระแนะนำให้ใช้

นี่คือวิธีที่คุณสามารถสคริปต์:

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

ใช้มัน

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

อีกวิธีคือใช้กลุ่มกระบวนการ

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

ใช้มัน

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

สังเกตว่าทั้งคู่ทำงานได้ก็ต่อเมื่อคำสั่งทางด้านซ้ายของไปป์ทำงานนานพอที่psจะเห็นได้ คุณบอกว่าคุณใช้มันด้วยtail -fดังนั้นฉันสงสัยว่านี่เป็นปัญหา

$ sleep 0 | ./whatpipe1.sh 

$ sleep 1 | ./whatpipe1.sh
sleep 1

แทนที่จะโพสต์ขนาดใหญ่นี้ฉันจะได้รับคำตอบที่ 2 กับสคริปต์ตาม lsof การทำงานที่ดีสำหรับคนนั้น
akira

@ Akira ขอบคุณ ใช้ความพยายามสองสามครั้งเพื่อทำให้มันสะอาดและพกพาได้ เรียนรู้เล็ก ๆ น้อย ๆ เกี่ยวกับ procfs และ lsof ระหว่างทาง ขอบคุณสำหรับความคิด
มิเคล

ฉันยอมรับของคุณเพราะมันให้คำตอบที่คนอื่นสามารถใช้ได้โดยตรง @ Akira คุณทำผลงานได้เป็นส่วนใหญ่ขอโทษด้วยที่ฉันไม่สามารถรับของคุณได้เช่นกัน
เซนต์จอห์นจอห์นสัน

10

ไปป์จะปรากฏเป็นรายการในรายการเปิดไฟล์ของกระบวนการของคุณ:

 % 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

คุณสามารถใช้บางสิ่งเช่น:

 % 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

ดังนั้นกว่าที่คุณมี inode ของไปป์ :) ตอนนี้คุณสามารถค้นหากระบวนการอื่น ๆ ที่อยู่ภายใต้/proc/ไปป์นั้นได้ จากนั้นคุณจะมีคำสั่งที่ส่งไปให้คุณ:

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

ในตัวอย่างนี้ประปาหอผู้ป่วยcat shใน/proc/29889คุณสามารถค้นหาไฟล์ที่เรียกว่าcmdlineบอกคุณสิ่งที่เรียกว่า:

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

เขตข้อมูลของบรรทัดคำสั่งถูกคั่นด้วย NUL ดังนั้นจึงดูน่าเกลียดเล็กน้อย :)


ฉันไม่รู้คำตอบที่จะยอมรับ @Akira คุณให้รายละเอียดที่แท้จริงเกี่ยวกับวิธีการตรวจสอบในขณะที่ @Mikel ให้สคริปต์แก่ฉัน วิธีทำให้สิ่งต่าง ๆ น่ากลัว + ยาก
St. John Johnson

1

นี่คือโซลูชันขนาดกะทัดรัดที่ใช้ความทันสมัยlsofในการกระจาย Linux ที่ทันสมัย:

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

สิ่งนี้แสดงรายการไฟล์จุดสิ้นสุด ( +E) ของ FD 0 ในกระบวนการเชลล์ปัจจุบัน ( -p $$ -a -d 0) จากนั้น จำกัด เอาท์พุทเป็น PID เท่านั้น ( -t) ทำให้ PIDs ทั้งสองด้านของไพพ์

โปรดทราบว่า:

  1. อาจจะมีมากกว่าหนึ่ง PID ที่พบในตอนท้ายของแหล่งที่มาเช่น{ echo Hi; sleep 5 ; } | whatpipe.shมีแนวโน้มที่จะให้ผลผลิตbash(subshell การป้อนข้อมูล) sleep 5และ
  2. +Eจะใช้ได้เฉพาะเมื่อถูกคอมไพล์ด้วยlsof -DHASUXSOCKEPTสิ่งนี้ควรเป็นจริงสำหรับ distros Linux ที่ทันสมัยที่สุด แต่ตรวจสอบการติดตั้งของคุณด้วย:lsof -v 2>&1 | grep HASUXSOCKEPT
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.