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.


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}- (assuming $gr is not IFS-splitted as you forgot the quotes and bash still does split+glob in redirections) 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.

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 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 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 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 at 22:52
  • 1
    @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 at 23:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.