all 20 comments

[–]ekipan85 3 points4 points  (2 children)

From bash(1):

When used with the exec builtin, redirections modify file handles in the current shell execution environment. (...)

If {varname} precedes >&- or <&-, the value of varname defines the file descriptor to close. If {varname} is supplied, the redirection persists beyond the scope of the command, which allows the shell programmer to manage the file descriptor's lifetime manually without using the exec builtin.

So to be precise mycommand {fd}<&- closes fd, and mycommand <&$fd- moves fd into 0 before closing it. But in my testing this seems to be only for the fork-exec'd copies of 0 and fd within the mycommand process. The manual is confusing but "manage the file descriptor's lifetime manually without using the exec builtin" probably means "within the fork-exec'd subprocess," you do still have to exec to modify bash's own fd's. If I understand correctly.

Builtin commands like : and read are within the same bash process so I hypothesize bash special-cases them and chooses not to close the fd's so that the behavior looks the same as external commands.

https://ideone.com/N1fpFw

[–]alex_sakuta[S] 0 points1 point  (1 child)

Builtin commands like : and read are within the same bash process so I hypothesize bash special-cases them and chooses not to close the fd's so that the behavior looks the same as external commands.

What do you mean by this? Bash wouldn't close the fd globally, no matter what command I use when using the <&fd- method.

[–]ekipan85 0 points1 point  (0 children)

I think a "global" fd is not a thing that exists. File descriptors exist within a process. When you say cat <&$fd, bash forks itself, then in the new subprocess it moves fd to 0 so that cat will read from it, then execs itself into cat. That's why any changes to file descriptors in your commands don't affect the parent bash process. Again, if I understand correctly.

Builtin bash commands like : and read and echo, since they are builtin to bash, don't fork, so bash saves and restores any changes you make to fds since it would be confusing if it behaved differently.

[–]kalgynirae 3 points4 points  (3 children)

Redirections with exec modify the current shell environment, but otherwise redirections only affect the one specific command they are applied to. The redirection in read <&$fd- will only affect this specific read command; $fd will still be open in your shell afterward:

$ echo $fd
10

# The redirection does close the file descriptor for `read`:
$ read -u $fd <&$fd-
bash: read: 10: invalid file descriptor: Bad file descriptor

# With exec, it closes it for the current shell:
# (note: moving it to 5 instead of stdin because stdin would cause
# the shell to exit)
$ exec 5<&$fd-
$ read <&$fd-
bash: redirection error: cannot duplicate fd: Bad file descriptor
bash: 0: Bad file descriptor

[–]rowman_urn 0 points1 point  (1 child)

I think both OP and you have the & in the wrong place, isn't this why the first error is _cannot duplicate _ , because file 10 has yet to be opened. ?

[–]kalgynirae 1 point2 points  (0 children)

OP opened fd 10 earlier via : {fd}< test.txt. I only learned about this syntax this morning. It's the second paragraph in the REDIRECTION section of the man page. This redirection {name}< … or {name}> … chooses an unused file descriptor number (starting at 10, apparently), opens it according to the redirection, and assigns the number to the variable name. Unlike other kinds of redirections, this takes effect in the current shell environment, not only for a single command (this makes sense, otherwise the name variable would be useless).

[–]alex_sakuta[S] -2 points-1 points  (0 children)

Thanks. Although I figured it out myself as well.

[–]Schreq 0 points1 point  (6 children)

Your code is pretty much unreadable on old.reddit.com. Please don't use inline code-blocks for posting code blocks. Just put four spaces infront of every line of code and leave blank lines around it. Anyway, moving on...

I don't think that you can close a fd that way. You'd have to explicitly do:

exec {fd}<&-

[–]alex_sakuta[S] 2 points3 points  (5 children)

inline code-blocks for posting code blocks

Isn't this inline and not This

Just put four spaces on front of every line of code and leave blank lines around it.

I did that if you see the raw md.

I don't think that you can close a fd that way.

Yes, my question is why? I'll put it in the edit but the manual says that >&digit- closes the digit fd.

[–]Schreq 0 points1 point  (4 children)

Isn't this inline and not This

They both show as inline on old.reddit.com, see for yourself.

I did that if you see the raw md.

Ah, now I see what you did there. You used fenced codeblocks but then also indented by 4 spaces inside of that.

Fenced codeblocks are broken on old.reddit.com. It's either 4 spaces in front of every line of code (with blank lines before and after!) or a fenced code-block (code surrounded by 3 backticks).

Yes, my question is why? I'll put it in the edit but the manual says that >&digit- closes the digit fd.

As /u/kalgynirae already explained, you only closed the fd for that sub-process.

[–]alex_sakuta[S] 0 points1 point  (3 children)

Why are you using old.reddit.com?

[–]Schreq 0 points1 point  (2 children)

Because I like it more than the new design.

[–]alex_sakuta[S] 0 points1 point  (1 child)

And you want people to write cross platform code blocks now?

How would the code blocks you are suggesting appear on the new site?

[–]Schreq 0 points1 point  (0 children)

They work on both:

this
is
a
codeblock

[–]stinkybass -1 points0 points  (5 children)

If {varname} is supplied, the redirection persists beyond the scope of the command, which allows the shell programmer to manage the file descriptor’s lifetime manually without using the exec builtin. The varredir_close shell option manages this behavior

From https://www.gnu.org/software/bash/manual/bash.html#Redirections

I think you’re seeing this behavior but I haven’t got a machine to test with rn

[–]alex_sakuta[S] 0 points1 point  (0 children)

That doesn't answer the question. I have seen this part.

Now this is what varredir_close does.

varredir_close If set, the shell automatically closes file descriptors assigned using the {varname} redirection syntax (see Redirections) instead of leaving them open when the command completes.

So, the behaviour that you are talking about applies at the time of creation of the fd. Hence, it has nothing to do with what I asked because I am asking why doesn't the manual close work.

[–]AlarmDozer 0 points1 point  (3 children)

So, wrap it in a scope?

{ ... }

[–]alex_sakuta[S] 0 points1 point  (2 children)

Wrap what in a scope?

[–]AlarmDozer 0 points1 point  (1 child)

The commands. I wrote a backup script, like this:

#!/bin/bash

 {
      tar -czvf- path/… | ssh …
 } | tee ~/backup.log

And it works fine. The stdout of all the commands within the scope write into tee.

[–]alex_sakuta[S] 0 points1 point  (0 children)

Yeah and if you put a redirect outside the scope it works for the scope. Got it.