Як ви визначаєте фактичну команду, яка проходить у вас?


9

Скажімо, у мене є сценарій bash під назвою log.sh. У цьому сценарії я хочу прочитати вхід з труби, але я також хочу знати команду, яка використовується для введення в мене труби. Приклад:

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

У скрипті оболонки я хочу знати команду tail -f /var/log/httpd/error.


Мені надзвичайно цікаво, чому ви цього хочете.
Ігнасіо Васкес-Абрамс

Я здогадуюсь, ти робиш якийсь тип графічного інтерфейсу, який фіксує та обробляє додатки?
palbakulich

Мені хотілося б знати, щоб я міг розрізнити, де розмістити результати. Залежно від команди та назви файлів, я хотів би виконувати різні дії.
Сент-Джон Джонсон

4
Це звучить як серйозне порушення Принципу найменшого сюрпризу для мене. Якщо сценарій повинен робити різні дії за різних обставин, тоді це має контролюватися опцією командного рядка, а не тим, звідки надходить його вхід.
Дейв Шерохман

@Dave, я згоден. Скажімо, заради цього прикладу, я просто хочу «знати», що таке команда, що надходить.
Сент-Джон Джонсон

Відповіді:


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

замість цього величезного допису я дав би другу відповідь зі сценарієм на основі lsof. приємна робота для цього.
акіра

@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

тож, ніж у вас є вкладення труби :), ви можете шукати будь-який інший процес під /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 дав мені сценарій. Спосіб зробити речі дивовижними + складними.
Сент-Джон Джонсон

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), даючи PID з обох сторін труби.

Зауважте, що:

  1. На кінці джерела може бути більше одного PID, наприклад { echo Hi; sleep 5 ; } | whatpipe.sh, ймовірно, вийде a bash(вкладений вкладиш) та sleep 5.
  2. +Eдоступний, лише якщо він lsofбув складений -DHASUXSOCKEPT. Це має бути справедливо для більшості сучасних дистрибутивів Linux, але все-таки перевірте свою установку за допомогою:lsof -v 2>&1 | grep HASUXSOCKEPT
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.