使用 bash 函数将有关自身的一些信息转储到 stderr,然后调用cat
形成管道,我如何知道管道中的哪个阶段调用了另一个阶段?
一种解决方案是为每个阶段提供一个名称,例如,,,,,,a
然后当我查看输出时我知道。但假设我不想说出各个阶段的名称。有没有一种方法可以查看阶段的文件描述符,并看到一个阶段是另一个阶段的文件描述符,这样我就知道前者正在通过管道传输到后者?如果是这样,我如何识别共享管道?我尝试过,但我不知道如何建立链条。b
c
d
a > b > c > d
stdout
stdin
lsof
例如,
printf '%s\n' main "$(lsof -p $$ | grep dev)" > /dev/stderr
pipe() {
printf '%s %s:%s %s\n' $1 $BASHPID $BASH_SUBSHELL $$ > /dev/stderr
printf '%s\n' $1 "$(lsof -p $$ | grep dev)" > /dev/stderr
cat
}
echo
echo hi | pipe a | pipe b | pipe c | pipe d
生产
main
bash 51147 Setup 0u CHR 16,2 0t476770 3717 /dev/ttys002
bash 51147 Setup 1u CHR 16,2 0t476770 3717 /dev/ttys002
bash 51147 Setup 2u CHR 16,2 0t476770 3717 /dev/ttys002
bash 51147 Setup 26u CHR 15,0 0t214349 579 /dev/ptmx
bash 51147 Setup 27u CHR 15,1 0t1206500 579 /dev/ptmx
a 51176:1 51147
d 51147:0 51147
b 51177:1 51147
c 51179:1 51147
b
bash 51147 Setup 1u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 2u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 26u CHR 15,0 0t214349 579 /dev/ptmx
bash 51147 Setup 27u CHR 15,1 0t1206500 579 /dev/ptmx
bash 51147 Setup 254u CHR 16,2 0t477304 3717 /dev/ttys002
d
bash 51147 Setup 1u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 2u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 26u CHR 15,0 0t214349 579 /dev/ptmx
bash 51147 Setup 27u CHR 15,1 0t1206500 579 /dev/ptmx
bash 51147 Setup 254u CHR 16,2 0t477304 3717 /dev/ttys002
a
bash 51147 Setup 1u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 2u CHR 16,2 0t477304 3717 /dev/ttys002
bash 51147 Setup 26u CHR 15,0 0t214349 579 /dev/ptmx
bash 51147 Setup 27u CHR 15,1 0t1206500 579 /dev/ptmx
bash 51147 Setup 254u CHR 16,2 0t477304 3717 /dev/ttys002
c
bash 51147 Setup 1u CHR 16,2 0t478702 3717 /dev/ttys002
bash 51147 Setup 2u CHR 16,2 0t478702 3717 /dev/ttys002
bash 51147 Setup 26u CHR 15,0 0t214349 579 /dev/ptmx
bash 51147 Setup 27u CHR 15,1 0t1206500 579 /dev/ptmx
bash 51147 Setup 254u CHR 16,2 0t478702 3717 /dev/ttys002
hi
但我没有看到for 中stdout
的标识符。a
stdin
b
答案1
管道是匿名的(与命名管道相反),因为它们是使用pipe(2)
命令。然而,它们当然有一个显示的内部 ID lsof
(您可以在 中看到ls -l /proc/<pid>/fd
)。
但我没有看到 stdout 的标识符
a
是 的 stdinb
。
这是因为您没有lsof
使用正确的流程。$$
扩展为脚本 PID,而不是管道命令创建的子 shell 的 PID。您必须使用$BASHPID
它来代替。
如此修改你的函数:
pipe() {
local pid=$BASHPID
printf '%s %s:%s %s\n' "$1" "$pid" "$BASH_SUBSHELL" $$ > /dev/stderr
printf '%s\n' "$1" "$(lsof -a -p "$pid" -d 0,1)" > /dev/stderr
cat
}
你现在会得到类似的东西:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 1290 xhienne 0r FIFO 0,12 0t0 180254229 pipe
bash 1290 xhienne 1w FIFO 0,12 0t0 180254230 pipe
...其中节点号是管道的内部 ID。然后您将能够注意到管道 #12345678 将 stdout 连接a
到 stdin b
,等等。