如何确定一个命令是否通过管道传输到另一个命令?

如何确定一个命令是否通过管道传输到另一个命令?

使用 bash 函数将有关自身的一些信息转储到 stderr,然后调用cat形成管道,我如何知道管道中的哪个阶段调用了另一个阶段?

一种解决方案是为每个阶段提供一个名称,例如,,,,,,a然后当我查看输出时我知道。但假设我不想说出各个阶段的名称。有没有一种方法可以查看阶段的文件描述符,并看到一个阶段是另一个阶段的文件描述符,这样我就知道前者正在通过管道传输到后者?如果是这样,我如何识别共享管道?我尝试过,但我不知道如何建立链条。bcda > b > c > dstdoutstdinlsof

例如,

    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的标识符。astdinb

答案1

管道是匿名的(与命名管道相反),因为它们是使用pipe(2)命令。然而,它们当然有一个显示的内部 ID lsof(您可以在 中看到ls -l /proc/<pid>/fd)。

但我没有看到 stdout 的标识符a是 的 stdin b

这是因为您没有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,等等。

相关内容