exec {ec}< <(echo "puts 'hello'") && sudo ruby /proc/$$/fd/${ec}

As soon as I close it with

exec <&"${ec}"-

The shell exits.

In another situation, I create this file descriptor:

exec {gr}> >(/usr/bin/grep 'hello')

Then I close it exec >&${gr}-, and now when I run a simple command like ls, nothing is printed.

I can do ls >&2 and sure enough it shows something, but clearly stdout is now MIA.

I'm still learning how all this file descriptor business works, and would like some guidance. Thanks.


1 Answer 1


Bash's manual says:

If >&- or <&- is preceded by {varname}, the value of varname defines the file descriptor to close.

So, it's exec {fd}>&- (or {fd}<&-) to close it.

$ exec {fd}>foo.out
$ echo "$fd"
$ echo hi >&"$fd"             # works
$ exec {fd}>&-                # close it
$ echo ho >&"$fd"             # doesn't work any more
bash: "$fd": Bad file descriptor
$ cat foo.out

exec <&"${ec}"- moves the fd named in $ec to stdin (i.e. makes stdin a copy of fd $ec, then closes fd $ec). The shell now has its stdin connected to the process substitution, and continues reading input from there. But ruby probably already read everything, and the pipe would just give an EOF, exiting the shell.

Similarly, exec >&${gr}- redirects stdout to the fd stored in $gr (closing $gr). Any further regular output, like that from ls goes to the process substitution and the grep. So, ls might not produce any visible output, but echo hello should.

Though as usual, $gr here should be also quoted to avoid word splitting, since in general, IFS can contain digits. So that would be better written as exec >&"${gr}"-.

The fd number you want to modify comes first in the redirections, the default being stdin (0) for < and stdout (1) for >.

  • Yea the sudo ruby <(echo "puts 'hello'") doesn't work. It is from this answer. The only way it worked was the way I had it in my question. I was going for a one-liner
    – smac89
    Apr 16, 2021 at 22:45
  • So you're saying that if I do something like exec 1>&7-, bash will simply parse this to mean "close fd 1", and just ignores the 7 in there? But without the -, it means, redirect fd 1 to fd 7? Seems like quite the jump in reasoning, but I guess it makes sense
    – smac89
    Apr 16, 2021 at 22:47
  • 1
    @smac89, oooh, right, it's /proc/$$/fd/, not /proc/self/fd/, so you're letting ruby "steal" the fd from the shell's fd list. That's... somewhat convoluted, but ok, I suppose it works.
    – ilkkachu
    Apr 16, 2021 at 22:50
  • @smac89 1>&7 would mean to fd 1 a copy of fd 7. 1>&- would close fd 1. I'm not at all sure what 1>&7- should be. Oh, should have read the manual, it moves the fd from 7 to 1.
    – ilkkachu
    Apr 16, 2021 at 22:52
  • 2
    @smac89, not quite that either. Let's take something similar to the first one. exec {ec}< <(echo "echo bye"). Say ec gets the number 9, just to pick one. Now, fd 9 is connected to the process substitution. Now, don't do anything else with $ec, just run the exec <&$ec-, same as exec 1<&9-. That says to move 9 to 1 (stdin), so now 1 is connected to the process substitution and 9 gets closed. Then the shell reads input from 1, now the process substitution. It reads echo bye, runs that, gets an end-of-file, and exits.
    – ilkkachu
    Apr 16, 2021 at 23:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .