問題

例えば、

./script.sh | less

の際に、script.sh内からlessのPIDを取得したい時。

取得する

pipe=$(readlink /proc/$$/fd/1 | sed -e 's/pipe:\[\([0-9]*\)\]/\1/g')
pid=$(lsof -n -P -u $USER 2> /dev/null | awk "{if (\$4 ~ 0r && \$8 ~ $pipe) print \$2}")

流れ

/proc/$$/fd/1は、標準出力(すなわちパイプ)のファイルデスクリプタで、これの実体はpipe:[XXXXXX]みたいなファイル(パイプ)。
これの名前をreadlinkで読んで、sedXXXXXXの部分を取り出しています。($pipe)

さて、lsofで開かれているファイル(パイプ含む)の一覧が出てくるので、エラーを捨てつつ先程のパイプ番号を含む行の出力側のPIDをゲット。($pid)

lsofは遅いので、ユーザーを-uで絞ることで高速化を図りました

注意

出力がパイプされている前提なので、もし実際に使う場合は確認を挟んだほうがいいと思いました

if [ -t 1 ]; then
  echo "Not piped..."
else
  echo "Piped..."
fi

下記環境で検証していますが他の環境(macとか)だと動かないかもしれません

  • linux 4.15.7-1-ARCH
  • bash 4.4.19
  • lsof 4.90