在管道中多次交换 STD{OUT,ERR}

在管道中多次交换 STD{OUT,ERR}

基于这个答案,我编写了以下内容来交换文件描述符 1 和 2:

swap12:

#!/bin/bash
"$@" 3>&1 1>&2 2>&3 3>&-

然后我可以在管道中对 STDERR 进行操作,例如:

$ swap12 ls -ld /tmp /ooooooo | tr o X
ls: cannXt access '/XXXXXXX': NX such file Xr directXry
drwxrwxrwt 19 root root 1400 Jul  1 17:14 /tmp

然而,多次交换 FD 并不起作用:

$ swap12 ls -ld /tmp /ooooooo | swap12 tr o X | tr o Z
ls: cannXt access '/XXXXXXX': NX such file Xr directXry
drwxrwxrwt 19 root root 1400 Jul  1 17:14 /tmp

上面我期望第二个swap12再次交换 STDOUT 和 STDRR,因此第二个将在的原始 STDOUTtr上运行。ls我期待看到:

$ swap12 ls -ld /tmp /ooooooo | swap12 tr o X | tr o Z
ls: cannXt access '/XXXXXXX': NX such file Xr directXry
drwxrwxrwt 19 rZZt rZZt 1400 Jul  1 17:14 /tmp

我怎样才能实现我所追求的目标?

我感觉我的问题是由于更改子 shell 中的文件描述符造成的。作为zsh全局别名实施会有优势吗alias -g? (但是bash实施起来会是什么样子呢?)

答案1

通常情况下,stderr直接进入终端,stdout进入管道:

          ls stdout -->
ls -ld /tmp /ooooooo | tr o X
 |
 v ls stderr (to terminal)

交换它们后,stdout转到终端,而不是管道:

                 ls stderr -->
swap12 ls -ld /tmp /ooooooo | tr o X
        |
        v ls stdout

交换stdout/不以任何方式涉及结果,stderr因为它之前已从管道重定向。trstderrls

                 ls stderr -->   tr stderr -->
swap12 ls -ld /tmp /ooooooo | swap12 tr o X | tr o Z
        |                             |
        v ls stdout                   v tr stdout

如果你想单独处理stdoutand stderrof ls,你可以使用进程替换(应该在 Bash 和 Zsh 中工作):

$ ls -ld /tmp /ooooooo 2> >(tr o X)  > >(tr o Z) 
ls: cannXt access '/XXXXXXX': NX such file Xr directXry
drwxrwxrwt 25 rZZt rZZt 4096 Jul  1 14:40 /tmp/

答案2

管道已连接ls.stderrtr1.stdin。然后交换tr1.stdoutandtr1.stderr并通过管道连接tr1.stderrtr2.stdintr2 发现没有os,所以什么也不做。

第二个swap12不应该也不会撤销第一个。

你打算这样做吗./swap12 ./swap12 ls -ld /tmp /ooooooo | tr o X?这将撤消交换。

相关内容