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


11

如果在中bash执行:

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

哪里cmd{1..n}可能没有区别,我该如何获取PID cmdi?或者,我如何发信号通知该cmdi过程?(例如,发送SIGUSR1?) pkill/ pgreppidof等看起来不像很好的答案,因为其他情况下cmdi或许运行,包括作为同一管道的一部分。jobs -pcmd1我的PID 。

i可以是{1..n}



1
@ G-Man Care解释一下?我只看到表面上的相似之处,正如我在Ramesh的答案中所解释的那样,修改命令集没有多大用处。
muru 2014年

表面相似?cat /var/run/out | nc -l 8080仅仅是表面上类似cmd1 | cmd2?您想要输入裸管道然后恢复PID的约束是(1)在问题中未提及,并且(2)不太可能提供好的通用解决方案。
G-Man说'Resstate Monica''Sep

@ G-Man相反,您要施加的约束条件是没有说明简单的约束。cmd1 | cmd2这是非常容易获得两个PID的特殊情况。我有没有说过关于n的话?那么,为什么要假设n = 2?我说了什么是cmdi吗?那么,为什么要假设我可以修改cmdi?我要求一个一般的解决方案,您正在施加限制。
muru 2014年

Answers:


6

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

foo | bar | baz &
baz_pid=$!

无法轻松访问其他进程的PID。

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


您是否介意我也编辑问题以在列表中索取任意命令的PID?还是我应该开始一个新问题?
muru 2014年

您可能需要等待更长的时间才能找到答案。我对

没关系,眼前的问题已经解决,现在好奇心已成定局。我将对其进行编辑。因为我看到问题已经急剧改变,而较旧的答案看起来很不合时宜,所以请多加注意。
muru 2014年

@muru-请注意,最好将它命名为新的Q。
slm

@slm正确记录。将来会这样做。
大师

4

我认为您可以按照此处的建议进行操作。

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

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


有趣,但这将涉及修改命令集。一旦执行了命令,就没什么用了。
muru

@muru-什么?一旦执行完毕,PID有什么用?您是否需要管道的PID?jobs -p。用发出信号SIGPIPE。您想要吗cmdi-这个。
mikeserv

1
@mikeserv如果它们在后台运行,就不会像我们所说的那样运行。我应该通过哪种魔术来修改命令行?
大师

1
@muru会成为巫术。您需要一个调试器。
mikeserv

我发现这是启动后台进程,等待它们达到某种状态然后杀死它们的有用模式。如果有人感兴趣:gist.github.com/MatrixManAtYrService/…–
MatrixManAtYrService

2

一个不是非常便携式的,特定于Linux的解决方案可能是使用连接它们的管道来跟踪进程。我们可以获取管道中first(jobs -p)和last($!)命令的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

出于好奇,这里有更多关于这种事情的信息: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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.