This patch will fix an issue with redirections in
pipelines within rc functions.
--
If inside an rc function a pipeline is used that
contains a redirection, this might not work as
expected in (sub) shells that import the function
from environment.
For example, at an rc prompt, type:
fn f{
echo -n $pid:
echo >/dev/null | echo -n wor
{echo failed >[1=2]} >[2=1] | sed 's/failed/ks!/'
}
f # should work
rc -c f # will fail
First `f' is executed by the shell which parsed
function f. Then f is exported to the rc
subshell, imported by it, and executed.
Here the ">/dev/null", or ">[2=1]" sequences
will not work as expected.
What happens is that the meaning of the
redirections change while the function definition
is processed. When the function is exported by
addenv(), a string fn[pc-1].s is printed into
/env/fn#...
This string got constructed by emits(fnstr(...))
in /sys/src/cmd/rc/code.c:173, which calls
pfmt("%t"), which will put together the string
calling pcmd() as needed. In other words, the
`tree' structure is translated back into a string.
Since the redirections come earlier in the tree,
they will be printed first, so when the function
is translated into a string, it will look like
...
>/dev/null echo | ...
>[2=1] {echo failed >[1=2]} | ...
Note that the redirections now come first, and
have got a different meaning: they will apply to
the whole pipeline, not to the first command only.
This is because of the definitions in syn.y:
| cmd PIPE cmd
| redir cmd
For a redirection at the beginning of a line
the whole pipeline looks like one `cmd'.
If a subshell later imports f from /env/fn#f, it
obviously will get a function different from the
initially defined one.
Two probable solutions came to my mind,
1. in pcmd(), for the cases DUP and REDIR
change the order "c1" and the redirection
sequences are printed, so that the example
lines would be exported as they were
defined:
echo >/dev/null | ...
{echo failed >[1=2]} >[2=1] | ...
It turned out that this is not so easy, as
in case of multiple redirections they
would be produced in reversed, i.e.
wrong, order.
2. In "case PIPE:", enclose %t within braces,
so that a pipeline
cmd >/dev/null | cmd2
would be exported as
{>/dev/null cmd} | cmd2
To avoid an increasing number of braces
growing with each subshell started, it
could be tested whether there is already a
brace present, like this:
case PIPE:
- pfmt(f, "%t|", c0);
+ pfmt(f, c0->type==BRACE? "%t|": "{%t}|", c0);
#1 did not work for me, #2 produces the expected
results, as can be checked with the initial example.
|