获取后台管道命令序列中任何命令的 PID

获取后台管道命令序列中任何命令的 PID

如果在 中bash,我执行:

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

其中cmd{1..n}可能不明显,我如何获得 PID cmdi?或者,我如何发出cmdi进程信号? (例如,发送它SIGUSR1?) pkill/pgreppidof看起来不是很好的答案,因为cmdi可能正在运行其他实例,包括作为同一管道的一部分。为我jobs -p提供 , 的 PID cmd1

i可以是 中的任何内容{1..n}

答案1

对于问题的原始版本,当只需要最后一个命令的 PID 时,特殊变量$!是完美的。

foo | bar | baz &
baz_pid=$!

没有类似的方法可以轻松访问其他进程的 PID。

$pipestatus添加(zsh) 和(bash)花了很长时间$PIPESTATUS,最终让我们能够访问管道中的所有退出状态,以及$?自原始 Bourne shell 以来一直存在的最后一个退出状态。也许最终会发生类似的事情$!

答案2

我认为你可以按照建议做一些事情这里

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

在上面的示例中,我检索了第三个管道进程的 pid,并将其记录到文件 pid 中。我可以记下任何管道过程。

答案3

一个不太便携的 Linux 特定解决方案可能是使用连接进程的管道来跟踪进程。我们可以获取管道中第一个 ( jobs -p) 和最后一个 ( ) 命令的 PID 。$!使用任一 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

答案4

我在此代码中使用从零开始的数组。只是要小心你所经过的东西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

相关内容