Reputation: 22291
I'm writing a shell script S which calls a program P.
P accepts its input from stdin only.
One of the parameters of my script S is optional; if the parameter is present, it denotes a file, which should be fed into P. If the parameter is missing, the stdin of S should be fed into P.
This was my attempt to solve it:
P <${3:-&0}
The idea was: If the (3rd) parameter is a filename, say: foo, it should execute
P <foo
but if it is missing or empty, it should execute
P <&0
which, to my understanding, should be equivalent to simply
P
i.e. read from stdin. This doesn't work, however. While using <&0 literally, works, as in
echo abc|cat <&0
it would produce the error message no such file or directory: &0 when used in the way I'm doing it in my script. This surprises me, since I from the zsh man page, I understand that parameter expansion occurs before setting up the redirection.
Of course I could use the trick
cat $3|P
but this doesn't look very elegant.
Could someone explain to me, why my approach did not work, and what would be a good idiom to use here?
Upvotes: 1
Views: 195
Reputation: 107809
Parameter expansion happens before redirection, but that doesn't help. <&0
is the <&[NUMBER]
operator, whereas <${…}
is the <
operator followed by the word ${…}
which the <
operator interprets as a file name. There's no special case that would change the meaning if the file name begins with &
.
You can use the exec
builtin to set up a redirection and nothing else, so as to avoid duplicating code. This works well when the command is an external command.
( if [[ -n $3 ]]; then exec <$3; fi;
exec P )
Another approach if you need P
to be executed in the outer context (as opposed to a subshell) is to set up the redirection using an intermediate file descriptor.
if [[ -n $3 ]]; then exec 9<$3; else exec 9<&0; fi
P <&9
exec 9<&-
Alternatively, define a function to avoid code duplication.
P () { … }
if [[ -n $3 ]]; then P <$3; else P; fi
Upvotes: 1