如果在 中bash
,我执行:
cmd1 | cmd2 | ... | cmdi | ... | cmdn &
其中cmd{1..n}
可能不明显,我如何获得 PID cmdi
?或者,我如何发出cmdi
进程信号? (例如,发送它SIGUSR1
?)
pkill
/pgrep
等pidof
看起来不是很好的答案,因为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