[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: {varname} redirection for a command or group leaves the file open

From: George
Subject: Re: {varname} redirection for a command or group leaves the file open
Date: Mon, 29 May 2017 23:33:46 -0400

On Mon, 2017-05-29 at 15:36 -0400, Chet Ramey wrote:
> > 
> >  > Well, that's disappointing. So there is no technical reason for
> > this behavior 
> >  > other than copying the behavior of ksh. BTW zsh does the right
> > thing and in the 
> >  > following scenario: 
> >  > 
> >  > ls -lh /proc/self/fd {fd}>/dev/null 
> >  > 
> >  > and it closes the file descriptor after the command has completed.
> Face it: there's no real reason to implement this construct, period, and
> especially little reason for the way the Korn shell does it.  A script
> can trivially emulate the ksh usage by picking the file descriptor and
> performing the variable assignment itself; this construct is minimal
> syntactic sugar.
The way Korn shell does it makes sense, it is consistent with the way other 
file redirections work. Normally redirecting any command applies just to
that command, and not to surrounding code. "exec" is the exception to the rule, 
a redirection that stays open. (If there was no reason to implement
the construct, why was it implemented?)
And it is not "trivial" for a shell script to even find an unused file 
descriptor EXCEPT by using the dynamic FD assignment syntax, which does it for
you. And even if your shell code could find a vacant file descriptor to use, 
you can't do this:
$ fd=$(find_vacant_fd)
$ exec $fd<file    #errors out - $fd is interpreted as a command name rather 
than a FD number. Best you can do is:    $ eval "exec $fd<file"
Which means that without dynamic FD redirects, you are pretty much limited to 
statically-assigning file descriptors you use in the shell anyway,
unless you use "eval".
I think dynamic file descriptor assignment is a much better programming pattern 
than relying on numeric file descriptors: particularly when used with
"exec" because opening a file with a numeric "exec" redirect clobbers whatever 
file descriptor was in that slot. With command redirects it's a bit
less important because redirections applied to a command are undone after 
running that command (so even if you used a numeric FD that was already in
use, it would be automatically preserved and restored after the command) - but 
it's still useful when redirections are applied to loops and the like:
while read line <&${notes_file}; do
# 107 lines later...
done {notes_file}<NOTES {readme_file}<README
Instead of the first line being something meaningless like "while read line 
<&18" and having to scroll down 107 lines to find out what "18" refers to,
it's got a meaningful identifier right there. The programmer doesn't have to 
care what numeric file descriptor they got from open(). They SHOULDN'T
have to care. That's what variables are for.
I think Korn Shell made the right choice in retaining the limitation that only 
file descriptors 0-9 can be redirected by number. It's limiting in a
way, but not in a way that usually matters. How many commands do you run that:
1: use a file descriptor (apart from stdio/tty) that it inherits from the 
parent process
2: expect this file descriptor to be in a specific numeric slot that you're not 
able to change via a command-line option, and
3: expect this file descriptor to have a number of 10 or higher?
Because assigning file descriptors greater than 10 by number is only useful if 
you need to serve the needs of such a command. In practice, I haven't
seen many commands that even fill the first criterion. (xterm comes to mind, 
but it uses a command-line argument to specify the FD number, so criteria
#2 and #3 don't apply)
Korn Shell's approach allows it to implement the backward-compatible behavior 
for file descriptors 0-9, while allowing a different (and better) set of
design decisions for auto-assigned file descriptors: Child processes do not 
inherit auto-assigned file descriptors unless you explicitly redirect
them, and there is no chance for a numeric redirect to replace a file table 
entry opened by the auto-assignment syntax. Numeric redirects are more
like a necessary evil for the sake of redirecting STDIO and providing backward 
compatibility, rather than a sensible model for how file handling
should be implemented in the shell.
> The way bash implements it offers features that are not available in any 
> other use.
It really doesn't. And Bash's behavior seems very inconsistent, even 
nonsensically so:
"cmd --fd=$fd {fd}<file" works much like any other redirection, except that the 
file descriptor is auto-assigned. This form is largely useless because
the binding of $fd is neither expanded before "cmd" is run, nor exported to 
cmd's environment
"(cmd --fd=$fd) {fd}<file" is the most useful form: the redirection and 
variable binding are both effectively local to the invocation. $fd can be used
as an argument to "cmd", and it will be expanded.
"{ cmd --fd=$fd; } {fd}<file" causes the redirection and the variable binding 
to outlive the command block to which the redirection is applied: This
is not consistent with other forms of auto-assigning redirections, or other 
forms of redirection in general.
"while ...; done {fd}<file" is similarly broken. Redirections applied to 
"while" loops normally last only for the duration of the "while" loop.
The two broken forms offer nothing that can't be accomplished otherwise. They 
behave the same as simply running "exec {fd}<file" first and then
running the command without the redirect. Retaining the ability to limit the 
scope of the file redirect (and the variable binding, for that matter!)
is a much more useful feature.
> While the not-closing aspects can be emulated using `exec', a file descriptor 
> manipulated with `exec' is close-on-exec. Bash offers a direct analogue
> to the open system call.
Even if that were true, it makes no sense. File redirections should not outlive 
the command they're attached to - unless the command is "exec". And
files opened with "exec" are not close-on-exec:
$ exec {fd}<README     # $fd is "A file descriptor manipulated with exec"...
$ ls -l /dev/fd/$fd       # The $fd file descriptor is inherited by "ls"
lr-x------ 1 tetsujin tetsujin 64 May 29 20:58 10 -> 
The file was opened with "exec" and didn't close-on-exec when "ls" was run. 
(CLOEXEC files are almost useless in Bash, because you have no way in the
shell to hand that file to another process. Even if you duplicate it with a 
redirection, the duplicate is set CLOEXEC as well.)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]