Получить PID любой команды в фоновой последовательности команд

11

Если bashя выполню:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

где cmd{1..n}может быть не ясно, как я могу получить PID cmdi? В качестве альтернативы, как я могу сигнализировать cmdiпроцесс? (К примеру, отправить его SIGUSR1?) pkill/ pgrep, И pidofт.д. не похожи на хорошие ответы, так как других случаях , cmdiвозможно , работает, в том числе в рамках одного и того же трубопровода. jobs -pдает PID cmd1, для меня.

iможет быть что угодно в {1..n}.

Мур
источник
возможный дубликат Как я могу получить pid процесса, начатого таким образом
G-Man Говорит 'Восстановите Монику'
1
@ G-Man Care, чтобы объяснить? Я вижу только поверхностное сходство, и, как я объяснил в ответе Рамеша, изменение набора команд не имеет большого смысла.
Муру
Поверхностное сходство? cat /var/run/out | nc -l 8080только внешне похож на cmd1 | cmd2? Ваше ограничение, заключающееся в том, что вы хотите набрать простой конвейер и затем восстановить PID, (1) не указано в вопросе и (2) вряд ли позволит найти хорошее общее решение.
G-Man говорит: «Восстановите Монику»
@ G-Man Напротив, вы накладываете ограничения, которые просто не заявлены. cmd1 | cmd2Это особый случай, когда оба PID легко доступны. Я говорил что-нибудь о п? Так почему вы предполагаете, что n = 2? Я говорил что-нибудь о том, что такое CMDI? Так почему вы предполагаете, что я могу изменить CMDI? Я прошу общее решение, а вы вводите ограничения.
Муру

Ответы:

6

Для оригинальной версии вопроса, когда требовался только PID последней команды, специальная переменная $!является идеальной.

foo | bar | baz &
baz_pid=$!

Нет такого же легкого доступа к PID других процессов.

Потребовалось много времени для добавления $pipestatus(zsh) и $PIPESTATUS(bash), что в итоге дало нам доступ ко всем состояниям выхода в конвейере, в дополнение к $?последнему состоянию, которое существовало со времени первоначальной оболочки Bourne. Может быть, что-то аналогичное произойдет в $!конце концов.


источник
Вы не возражаете, если я отредактирую вопрос, чтобы запросить PID произвольной команды в списке? Или я должен начать новый вопрос?
Муру
Вам, вероятно, придется ждать намного дольше, чтобы получить ответ на этот вопрос. У меня нет сильных чувств относительно организации сайта stackexchange, поэтому отдельный вопрос, вопрос редактирования, что угодно ... меня не беспокоит
Это нормально, насущная проблема решена, теперь любопытство отвечает. Я отредактирую это тогда. Просто хедз-ап, так как я видел, как вопросы резко меняются, и старые ответы выглядят очень неуместно.
Муру
@muru - обратите внимание, что было бы лучше сделать его новым Q, ссылающимся на этот.
СЛМ
@ должным образом отметил. Сделаем так в будущем.
Муру
4

Я думаю, что вы могли бы сделать что-то, как предложено здесь .

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Здесь, в приведенном выше примере, я получил pid третьего процесса и записал его в файл pid. Я мог бы записать это для любого процесса.

Рамеш
источник
Интересно, но это будет включать в себя изменение набора команд. Не так много пользы, как только команды были выполнены.
Муру
@ Муру - что? какая польза от любого PID после его завершения? Вы хотите PID трубопровода? jobs -p, сигнализировать с помощью SIGPIPE. Хочешь cmdi- это.
mikeserv
1
@mikeserv Нет, если они на заднем плане, бегут, как мы говорим. Каким колдовством я должен изменить командную строку для этого?
Муру
1
@muru это было бы колдовством. тебе нужен отладчик
mikeserv
Я считаю, что это полезный шаблон для запуска фоновых процессов, ожидания их достижения определенного состояния и последующего их уничтожения. В случае , если кто -то интересуется: gist.github.com/MatrixManAtYrService/...
MatrixManAtYrService
2

Не очень переносимым, специфичным для Linux решением может быть отслеживание процессов с использованием каналов, которые их соединяют. Мы можем получить PID первой ( jobs -p) и последней ( $!) команд в конвейере. Используя любой PID, этот скрипт может выполнить эту работу:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done
Мур
источник
Для любознательных, есть больше о такого рода вещи здесь: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

Я использую нулевые массивы здесь, в этом коде. Просто будь осторожен с тем, что проходишь мимо eval.

#!/bin/bash

cmd=('sleep 10' 'sleep 2' 'sleep 5')
first=1
for c in "${cmd[@]}"; do
  ((first)) && { pipe=$c; first=0; } || pipe+='|'$c
done
shopt -u lastpipe
eval $pipe &

printf 'Pipe:\n%s\n\n' "$pipe"

shellpid=$BASHPID
parent=$(ps -o pid= --ppid $shellpid | head -n -1)
declare -a pids=()
mapfile -t pids < <(printf '%s\n' $(ps -o pid= --ppid $parent))
printf '%s\n' 'Listing the arrays:'
printf '%2s %6s %s\n' i PID command
for i in "${!cmd[@]}"; do
    printf '%2d %6d %s\n' "$i" "${pids[i]}" "${cmd[i]}"
done

printf '\n%s\n' 'ps listing:'
ps xao pid,ppid,command | head -n 1
ps xao pid,ppid,command | tail | head -n -3
Ярно
источник