nash icon indicating copy to clipboard operation
nash copied to clipboard

Design: IO redirections

Open geyslan opened this issue 8 years ago • 4 comments

@tiago4orion and folks,

First a brief explanation for the later proposals.

nash uses a pattern of redirections similar to plan9 rc:

A file descriptor may be redirected to an already open descriptor by writing >[fd0=fd1] or <[fd0=fd1]. Fd1 is a previously opened file descriptor and fd0 becomes a new copy (in the sense of dup(3)) of it. A file descriptor may be closed by writing >[fd0=] or <[fd0=]

Until now lacking support to input and append redirections.

Well, the rc way uses as prefix > (greater than) or < (less than) which define the stream flow of the output and input respectively, followed by [] brackets which involve the fd redirection per si. Examples from README:

# stdout to log.out, stderr to log.err
λ> ./daemon >[1] log.out >[2] log.err
# stderr pointing to stdout
λ> ./daemon-logall >[2=1]
# stdout to /dev/null
λ> ./daemon-quiet >[1=]
# stdout and stderr to tcp address
λ> ./daemon >[1] "udp://syslog:6666" >[2=1]
# stdout to unix file
λ> ./daemon >[1] "unix:///tmp/syslog.sock"

My proposal is based on the left-to-right reading pattern that's well-known by the pipe style. So left-to-right should be the rule to be followed.

Below I'll show how redirections are in nash and how I would like it to be.

./daemon >[1] output Would be done too with a single > ./daemon > output

# stdin redirection not implemented yet Would be done with a single < cat < output

since reading from a file should be the unique sane case of use. Or have you plans to make a option like Here Strings?

I will now break the nash (rc) way of think about redirections following the left-to-right rule.

./daemon >[1] output Would be ./daemon [1]> output

Why that? Let's do a laic human read in the above examples, in natural order (people of the world that read from right to left, I'm sorry but you can make a shell right for you).

./daemon >[1] output

  • Execute ./daemon
  • Redirect > output of execution to stdout 1 what sounds redundant
  • What is output?

./daemon [1]> output

  • Execute ./daemon
  • Get [1] execution stdout and redirect > to somewhere
  • output is naturally the above redirection receptacle

You probably may be shouting "but [1]> is similar to bourne 1>!". Actually it is. But we are still borrowing the rc brackets which are useful to denote the fds at the same time that we're making the readbility more easy for everyone that read from left to right.

There's a case that breaks the left-to-right rule.

cat <[0] input (rc way not implemented yet)

  • Execute ./daemon
  • Redirect < something ahead to execution stdin from stdin [0]
  • input is the source?

Reading it left-to-right makes no sense.

So in stdin redirection the cmd should be read from right-to-left.

  • Open input
  • Get [0] stdin from something ahead
  • Write it redirecting it < to cat execution

That's better, not perfect, but better. The more sane way of reading it would be cat [0]< input

  • Open input
  • Write it redirecting it < to something ahead
  • The destination would only be the stdin [0] of some execution
  • cat is the execution to receive it

And [0]< fits with the [1]> or [2]> as easier writing patterns since they follow the same structure open bracket, fd, close bracket, token redirection [fd]<|>.

When copying pointer between stderr, stdout and /dev/null, we need to use > only inside the brackets, since its usage outside doesn't give us any advantage.

./daemon >[2=1] Would be ./daemon [2>1]

Again you may be wondering why I'm resembling bourne redirection. Well, it's more straightful for me. Let's analyse them as before (left-to-right).

./daemon >[2=1]

  • Execute ./daemon
  • Redirect stdout to somewhere
  • 2 stderr is equal to 1

Well, what did happen here? Before to read three times the rc manual I really didn't get it. As I said the > simbology is in vain here. So let's ignore it. Let's focus only within the brackets. One more time I got confused. In the manual it says that redirections are evaluated from left to right, so I have assumed that 2 is equal 1, or 2 points to 1, but it's the same way of saying that I'm redirecting 2 to 1. rc way is messing with my brain parser :). I'm reading left-to-right but I have to make a assignment assumption inside the brackets that is quite annoying. Guys it's an equality, it isn't clear to everyone that 2 is spawning in 1; it could be the reverse, because 2=1 is the same as 1=2. I foresee headaches.

./daemon [2>1]

  • Execute /.daemon
  • Redirect stderr to stdout [2>1]

That proposal is crystal clear for me.

nash could have multiple redirections too like daemon [1,2>] that redirects 1 and 2 to /dev/null.

If the redirection have to go out to another destination besides 1,2,/dev/null it should be like this daemon [1,2]> outNerr

And why not to modernize those numbers creating letter aliases?

0   = i
1   = o
2   = e
    = n
./uniq [i]< input
./daemon [o]> log.out [e]> log.err
./daemon [o,e]> outNerr
./daemon [o>n] [e]> log.err

Cheers.

geyslan avatar Jan 28 '17 12:01 geyslan

Hello @geyslan,

My proposal is based on the left-to-right reading pattern that's well-known by the pipe style. So left-to-right should be the rule to be followed.

Ok.

since reading from a file should be the unique sane case of use. Or have you plans to make a option like Here Strings?

Some way of passing a multi line string as stdin could be added in the future, but I'm pretty sure it'll not be like heredoc.

./daemon >[1] output

  • Execute ./daemon
  • Redirect > output of execution to stdout 1 what sounds redundant
  • What is output?

You're making a very personal assumption here, but a valid one. You're parsing this sentence as you would parse borne shell. Below is how I interpret the example above:

  • Execute ./daemon
  • Redirect file descriptor 1 to ./output

I'm inclined to change this if would help readability for people coming from other shells.

When copying pointer between stderr, stdout and /dev/null, we need to use > only inside > the brackets, since its usage outside doesn't give us any advantage.

./daemon >[2=1] Would be ./daemon [2>1]

Ok, but how should be the syntax for "execute ./daemon and redirect both stderr and stdout to ./output" ?

As I said the > simbology is in vain here. So let's ignore it. Let's focus only within the brackets. One more time I got confused. In the manual it says that redirections are evaluated from left to right, so I have assumed that 2 is equal 1, or 2 points to 1, but it's the same way of saying that I'm redirecting 2 to 1. rc way is messing with my brain parser :). I'm reading left-to-right but I have to make a assignment assumption inside the brackets that is quite annoying. Guys it's an equality, it isn't clear to everyone that 2 is spawning in 1; it could be the reverse, because 2=1 is the same as 1=2. I foresee headaches.

You're making so much expectations. The symbol '=' here is not an equality, it's defined in the rc manual:

File descriptors may be replaced by a copy, in the sense of dup(2), of an already-open file by typing, vc junk.c >[2=1] That's the definition of [fd1=fd2]. When you make 2=1, you're setting /dev/stderr to a copied (dup(2)) version of /dev/stdout. And [1=2] is different than [2=1] (in this definition).

Using the very same argument, I can say that [1>2] doesn't make sense because it's an invalid comparison (1 is lower than 2). But... as we already use > with "redirection" meaning, maybe less confusion for novices.

Again, I agree that defining '=' could lead to confusion. We can improve in your way.

./uniq [i]< input ./daemon [o]> log.out [e]> log.err ./daemon [o,e]> outNerr .> /daemon [o>n] [e]> log.err

That's cryptic to me.

Thanks for your help!

i4ki avatar Feb 06 '17 00:02 i4ki

@tiago4orion,

You're making a very personal assumption here, but a valid one. You're parsing this sentence as you would parse borne shell.

Actually I'm making the left-to-right assumption that bourne shell uses aswell.

Below is how I interpret the example above: Execute ./daemon Redirect file descriptor 1 to ./output

Well, your interpretation doesn't follow entirely left-to-right reading.

Ok, but how should be the syntax for "execute ./daemon and redirect both stderr and stdout to ./output"?

./daemon [1,2]> output or ./daemon [o,e]> output

You're making so much expectations. The symbol '=' here is not an equality, it's defined in the rc manual:

You almost got me here. =) I agree with you that the meaning of the equal operator '=' isn't equality in our context, it's an assignment instead. But this case is not so straightforward for a quick reading of the line. As I said, the user must have prior knowledge of the rc syntax. And, as a personal taste, even worse once I'll have to change the left-to-right reading logic.

Using the very same argument, I can say that [1>2] doesn't make sense because it's an invalid comparison (1 is lower than 2).

Well, here '>' and '<' mean an arrow which depict the way of the redirection per si. X > Y mean X flows to Y.

As I explained above the meaning was wrongly expressed by me when using '=', since the correct is to represent it as an assignment.

But... as we already use > with "redirection" meaning, maybe less confusion for novices.

I could extrapolate the goal of the proposal here to ensure a more logical reading not only for novices, but for everyone.

That's cryptic to me.

I still think that it can be handy for novices.

Again, I agree that defining '=' could lead to confusion. We can improve in your way.

Thanks. But until now we have only two public (almost) opposite opinions. It's crucial to hear more manifests. :+1:

I look forward opinions. :man_technologist:

geyslan avatar Feb 06 '17 12:02 geyslan

Well, your interpretation doesn't follow entirely left-to-right reading.

I don't think so:

./daemon >[1] output

./daemon := "execute ./daemon" > := "redirect" [1] = "file descriptor 1 to" output = "output"

Simple find-replace suffice to get the meaning. Left-to-right both to machine and humans.

As I explained above the meaning was wrongly expressed by me when using '=', since the correct is to represent it as an assignment.

It's not an assignment either kkk It's defined differently in the manual.

My point is: Currently, nash redirections are uniform. The syntax is defined as:

destination = ( filename | uri | variable ) .

redirect    = ( (">" destination) |                              /* ./daemon > output */
                (">" "[" unicode_digit "]" destination) |        /* ./daemon >[1] output */
                (">" "[" unicode_digit "=" unicode_digit "]") |  /* ./daemon >[2=1] */
                (">" "[" unicode_digit "=" "]") ) .              /* ./daemon >[1=] */

The syntax is uniform, in all cases. It starts with '>' then the file descriptor rule inside brackets, then the destination. There's little syntax cases to remember, that's why I never had to re-read rc manual after I learnt it.

But (if I'm not mistaken) the new syntax you propose is something like (only counting output redirection):

fd          = unicode_digit | unicode_letter .  /* 0, 1, 2, 3, ..., i, o, u */
destination = (filename | uri | variable) .    

redirect    = ( (">" destination) |                          /* ./daemon > output */
                     ("[" fd "]" ">" destination) |          /* ./daemon [1] > output */
                     ("[" { fd "," } "]" ">" destination) |  /* ./daemon [1,2] > output */
                     ("[" { fd "," } ">" "]" |               /* ./daemon [1,2>] */
                     ("[" fd ">" fd "]") .                   /* ./daemon [2>1] */

This way, depending on what you want, the syntax is different, there's more syntax cases to remember both for humans and machine.

In terms of readability, I'm biased in direction of rc and you for sh. I think this is normal, and I'm Ok with changes, but the language will be a bit more complex. Yeah, we need a third opinion =)

i4ki avatar Feb 06 '17 13:02 i4ki

Well, here goes my 50 cents. I'm not proficient not in shell or rc :-P, so it will basically be crap. I really like the symmetry on the grammar of the rc style, it is a appealing. I'm totally on favor of having left to right stuff, but I can't see how bash is better. For example:

exec >[1] output

means: exec redirect fd1 to output

Now:

exec [1]> output

means: exec fd1 redirect to output

The one that reads better to me is "exec redirect fd1 to output", it more close to natural language. I liked the idea of having better aliases, like:

exec >[out] lala
exec >[err] lala

Not sure if just one letter reads a lot better than just the number, a little more writing and it gets really explicit.

I'm not the biggest fan of the "=" although. I really find it confusing. I agree that you must understand the context where you are, but for redirecting one flow to another the "=" is very odd to me, perhaps I'm not just used to it. For example, right now while I'm writing this I'm not sure if:

exec >[2=1]

Is redirecting 2 to 1 or 1 to 2. If 2 = 1, then 1 = 2 too (thinking on math :P). It gets a mess in my head. Again, it depends on the context, but at least in my mind = has a meaning that gets me confused when it is used to that, it is hard to get around it, but perhaps it's just me...or someday I will get used. Don't use it a lot.

katcipis avatar Feb 25 '17 19:02 katcipis