0

Based on this answer, I wrote the following which swaps file descriptors 1 and 2:

swap12:

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

I can then operate on STDERR in a pipeline, eg:

$ 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

However swapping the FDs doesn't work multiple times:

$ 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

Above I'm expecting the 2nd swap12 to again swap STDOUT and STDRR, so the 2nd tr would operate on ls's original STDOUT. I'm expecting to see:

$ 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

How can I achieve what I'm after?

I have the feeling that my problem is due to changing the file descriptors in subshells. Would there be an advantage to implementation as zsh global alias -g alias? (But then how would the bash implementation look?)

Tom Hale
  • 30,455

2 Answers2

3

Normally, stderr goes directly to the terminal, and stdout goes to the pipe:

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

After you swap them, stdout goes to the terminal, and not to the pipe:

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

Swapping stdout/stderr of the tr doesn't involve the resulting stderr of ls in any way, as it was redirected away from the pipeline earlier.

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

If you want to process the stdout and stderr of ls separately, you could use process substitution (should work in Bash and 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/
ilkkachu
  • 138,973
0

The pipe has connected ls.stderr to tr1.stdin. You then swap tr1.stdout and tr1.stderr and pipe tr1.stderr to tr2.stdin tr2 finds no os so does nothing.

The 2nd swap12 should not and does not undo the first.

Were you intending to do ./swap12 ./swap12 ls -ld /tmp /ooooooo | tr o X. This undos the swapping.