コマンドのバックグラウンドでパイプされたシーケンス内の任意のコマンドのPIDを取得します


11

bash、私が実行すると:

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

どこcmd{1..n}明瞭ではないかもしれないが、どのように私はのPIDを得るのですかcmdi?または、cmdiプロセスにどのように通知できますか?(例えば、それを送信SIGUSR1?) pkill/ pgreppidofなどの良い答えのように見ていない、の他のインスタンスので、cmdi多分、実行している同じパイプラインの一部として含みます。jobs -pのPID cmd1を私に与えます。

iの何でもかまいません{1..n}



1
@ G-Manケアの説明?私は表面的な類似性しか見ていません。Rameshの回答で説明したように、コマンドのセットを変更してもあまり役に立ちません。
muru、2014

表面的な類似性?表面的にcat /var/run/out | nc -l 8080だけていcmd1 | cmd2ますか?ベアボーンパイプラインを入力してPIDを回復するという制約は、(1)質問に記載されておらず、(2)優れた一般的なソリューションを可能にする可能性が低いです。
G-Manは「モニカの復活」を

@ G-Man逆に、単純なことは述べられていない制約を課しています。cmd1 | cmd2両方のPIDを簡単に取得できる非常に特殊なケースです。nについて何か言いましたか?では、なぜn = 2と想定するのでしょうか。私はcmdiとは何かについて何か言いましたか?では、なぜcmdiを変更できると思いますか?私は一般的な解決策を求めており、あなたは制限を課しています。
muru、2014

回答:


6

質問の元のバージョンでは、最後のコマンドのPIDのみが必要な場合、特殊変数$!は完全です。

foo | bar | baz &
baz_pid=$!

他のプロセスのPIDへの同様の簡単なアクセスはありません。

$pipestatus(zsh)と$PIPESTATUS(bash)が追加されるまでには長い時間がかかり$?、オリジナルのBourneシェル以来存在していた最後のステータスに加えて、パイプライン内のすべての終了ステータスにアクセスできるようになりました。多分同様のことが$!最終的に起こるでしょう。


リスト内の任意のコマンドのPIDも尋ねるように質問を編集してもよろしいですか?または、新しい質問を開始する必要がありますか?
muru 2014

おそらく、その答えが出るまでにはもっと長く待たなければならないでしょう。私はstackexchangeサイトの編成について強い感情を持っていないので、個別の質問、質問の編集など、何も気になりません

それで大丈夫です。差し迫った問題は解決され、好奇心が問われるようになりました。次に編集します。質問は劇的に変化し、古い回答は非常に場違いに見えるので、注意が必要です。
muru 2014

@muru-これを参照する新しいQにした方がいいでしょう。
slm

@slmは正式に言及した。将来的にそうするでしょう。
muru、2014

4

ここで提案されているようにあなたは何かをすることができると思います。

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

ここで上記の例では、3番目のパイプ処理されたプロセスのpidを取得し、それをファイルpidに書き留めました。パイプ処理されたプロセスについては、それを書き留めることができます。


興味深いですが、これには一連のコマンドの変更が含まれます。コマンドが実行されると、あまり使用されません。
muru 2014

@muru-なに?実行が終了したPIDはどのように使用されますか?パイプラインのPIDが必要ですか?jobs -p。とそれを合図SIGPIPE。欲しいですかcmdi-これ。
mikeserv 2014

1
@mikeservそれらがバックグラウンドにある場合は、話しながら実行します。そのためにコマンドラインを変更する必要がある魔法は何ですか?
muru、2014

1
@muru魔術になります。デバッガが必要です。
mikeserv 2014

これは、バックグラウンドプロセスを開始し、何らかの状態に達するのを待ってから強制終了するのに役立つパターンであると思います。場合には誰もが興味を持っている:gist.github.com/MatrixManAtYrService/...
MatrixManAtYrService

2

移植性が高くない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

好奇心旺盛な方のために、ここではこの種のことについてさらに詳しく説明します。unix.stackexchange.com
486233

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.