Как вы определяете фактическую команду, которая поступает в вас?


9

Допустим, у меня есть скрипт bash log.sh. В этом сценарии я хочу прочитать входные данные из канала, но я также хочу знать команду, используемую для передачи входных данных в меня. Пример:

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

В сценарии оболочки я хочу знать команду tail -f /var/log/httpd/error.


Мне очень любопытно, почему вы этого хотите.
Игнасио Васкес-Абрамс

Я предполагаю, что вы делаете какую-то программу с графическим интерфейсом, которая захватывает и обрабатывает pids?
Палбакулич

Я хотел бы знать, чтобы я мог различить, где поставить результаты. В зависимости от команды и имени файла, я хотел бы выполнять различные действия.
Сент-Джон Джонсон

4
Это звучит как серьезное нарушение принципа наименьшего сюрприза для меня. Если скрипт должен делать разные вещи при разных обстоятельствах, то это должно контролироваться параметром командной строки, а не тем, откуда поступает его ввод.
Дейв Шерохман

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

Ответы:


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 Спасибо. Потребовалось несколько попыток сделать его чистым и портативным. По пути узнали несколько вещей о procfs и lsof. Спасибо за идею.
Микель

Я принял ваш, поскольку он дает ответ, который могут использовать другие люди. @ Акира, ты сделал большую часть работы, извини, что я не смог принять и твою.
Сент-Джон Джонсон

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, поэтому выглядит немного некрасиво :)


Я не знаю, какой ответ принять. @ Акира, ты дал фактическую разбивку того, как это определить, в то время как @ Микель дал мне сценарий. Способ сделать вещи классными + сложными.
Сент-Джон Джонсон

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, вероятно, приведет к 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.