4

According to the manpage of bash, "redirection operators may precede or appear anywhere within a simple command or may follow a command."

Also according to the man page, "A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator."

Now here is the problem, what exactly does the shell mean by a simple command and a command? Because, /bin/echo foo {var}> somefile results in var not being assigned a fd. In contrast to that, echo foo {var}> somefile does result in a fd being assigned to var. It appears that, unlike with the command, it works with the builtin. I have noted that this kind of construction can appear anywhere in a command that uses a builtin: {var}> someFile echo foo works. In this context, if a simple command consists of builtins, therefore a word or a token is also a builtin? Because that's what the manpage says: "Word: A sequence of characters considered as a single unit by the shell. Also known as a token.""

I have also observed that fds get assigned when used with builtins and not external commands. So what is the problem here? Is my observation correct? And what exactly is the difference between a simple command and a command. The manpage is very vague about this.

4
  • 1
    You should describe your point. Since echo only writes to stdout, your code does not make sense. If you however are aksing an abstract question, you should describe what you like to achieve. – schily Sep 2 '20 at 8:24
  • It seems the redirection doesn't persist past simple commands. If you do { /bin/echo $foo; } {foo}>bar, both the redirection and $foo will persist. Probably some optimization gone overboard. – muru Sep 2 '20 at 10:17
  • 1
    Seems to me like a mistake where bash and zsh copied a ksh feature in an incompatible way. – Gilles 'SO- stop being evil' Sep 2 '20 at 10:28
  • As you may have discovered at this point, the distinction between simple and non-simple commands is not relevant here (that between builtins and external commands is). Manuals describing the shell language usually use the single word "command" in a generic way, implying that the command can be of different kinds (as opposed to e.g. "simple command" or "compound command"). The only formal definition of "command" I know about is the shell grammar in the POSIX shell language specification. – fra-san Sep 2 '20 at 11:44
2

From the top of my head, I'd guess it's because when running an external command, the shell 1) forks, 2) handles redirections in the child process, and 3) exec's the actual command. With the assignment to var done in 2, in the child, it's not visible to the parent shell after the launched program exits. With a builtin command, there's no fork, and the shell juggles the fd's as necessary in the main shell process, and the variable assignment takes effect there.

Not that that matters much, since the redirection in some external command {var}>/whatever is useless anyway. The external command can't know what fd was opened for it, and while it could determine what fd's it has, there might be others than the one opened for the redirection on this line, so it can't reliably use that fd to output to /whatever. Instead, you'd usually use a fixed fd number, or have some command line argument or environment variable to tell the fd number to use.

But you can't do that here either, since the variable isn't yet set when the expansions on the command line are processed so it's quite hard to pass it to the started program. unset var; /bin/echo "var=$var" {var}>/dev/null outputs just var= and so does unset var; var=$var /usr/bin/env {var}>/dev/null |grep ^var. (Though in ksh and zsh the latter seems to pass an actual number through the environment.)

The only place where that kind of redirection seems to make sense, is exec {var}>/whatever, and that being a builtin, the variable is set in the main shell, and the value is there for the following commands to use.

3
  • If this thing were implemented orthogonally, then export FD; external_command {FD}>file would work and would be useful. Basically, the first paragraph of this answer is right, the rest is just worthless rationalization. I fail to see how the fact that fd 5 doesn't stay open after echo 5>file, but $fd does after echo {fd}>file could be explained other than as an implementation glitch. – Cocaine Mitch Sep 2 '20 at 12:32
  • Two benefits for this method, though. (a) It returns an unused fd, so the coder does not need to track those he already used. (b) The fd gets a meaningful name, avoiding confusion. – Paul_Pedant Sep 2 '20 at 12:33
  • @localuser, that rationalization comes from the fact that I fail to see a use-case for it, other than exec, and you didn't provide one either. You do have a good point on the difference between echo 5>... and echo {fd}>..., though, and it nicely shows how what I wrote was indeed just a guess -- I didn't have the time to dig deeper into that. – ilkkachu Sep 2 '20 at 12:54
1

GNU bash documentation Sect 3.6 Para 2 says "Each redirection ...".

Regardless of whether the fd is actually used in a command like cmd {fd}>file, any built-in appears to set fd, and any external command appears to not set fd. Either bash or the guide is wrong.

Paul--) echo "Hello, Worms" {var}>real 1>&${var}
Paul--) declare -p var; ls -l real; cat real
declare -- var="11"
-rw-r--r-- 1 paul paul 13 Sep  2 10:27 real
Hello, Worms
Paul--) 
Paul--) /bin/echo "So long, suckers" {why}>deal 1>&${why}
Paul--) declare -p why; ls -l deal; cat deal
bash: declare: why: not found
-rw-r--r-- 1 paul paul 17 Sep  2 10:29 deal
So long, suckers
Paul--) 

In fact, the value of $why is available in the second redirection (of stdout), but not as specified in the Guide: "If {varname} is supplied, the redirection persists beyond the scope of the command...".

The only benefit of this kind of assignment seems to be that it assigns a free fd by name, instead of the coder having to keep track numerically:

: 9>myFirstFile
cmd >&9

: {fdLog}>myLogFile
cmd >&${fdLog}

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.