"our intuitive notion of order of operations on its piping" -- I don't know what your intuitive notion is, and there is no piping in your example. Piping would be:
sleep 5 | echo hello (this is an odd example, but it works)
I also don't see any race condition in your example. A race would be two processes accessing the same data.
They are redirects, and the way redirects like > and < work is to:
1. open a file
2. change the global process state
3. run arbitary commands, either builtin (echo) or external (ls). External commands require a new process and inherit their process state (descriptor table, where stdout and stdin are conected to) from the parent shell.
4. restore the global process state
5. close the file
If you understand that, it might be clear why the file test.txt is opened before sleep. There's really no other way it can work, given the fact that you can execute arbitrary commands inside the redirect.
I address some related things about redirects here: "Avoid Directly Manipulating File Descriptors in Shell"
The more unintuitive thing about shell is that what you wrote is technically better as:
{ sleep 5 && echo hello; } > test.txt
() doesn't mean "grouping" like in other languages, it means subshell. {} means grouping, and it has an odd way of parsing, where ; is required before }, but it isn't required before ). This is because () are operators and {} aren't.
I wrote about this here, "the subshell construct () is confused with the grouping construct { }":
EDIT: I will say that I was very confused by the syntax of > and < when I started learning shell. It does seem like ">" means "this goes into that". In fact I think I internalized that from MS-DOS.
But that doesn't explain what these mean, and why they are equivalent:
tac >out.txt <in.txt
tac <in.txt >out.txt
They are best read as prefix operators that modify the state of the process invocation.
What's even more confusing is that you can do:
tac 1>out.txt 0<in.txt
tac 0<in.txt 1>out.txt
Then they look like binary operators again. But the descriptor is "stuck on", it's actually part of the operator lexically.
They are redirects, and the way redirects like > and < work is to:
1. open a file
2. change the global process state
3. run arbitary commands, either builtin (echo) or external (ls). External commands require a new process and inherit their process state (descriptor table, where stdout and stdin are conected to) from the parent shell.
4. restore the global process state
5. close the file
If you understand that, it might be clear why the file test.txt is opened before sleep. There's really no other way it can work, given the fact that you can execute arbitrary commands inside the redirect.
I address some related things about redirects here: "Avoid Directly Manipulating File Descriptors in Shell"
http://www.oilshell.org/blog/2017/08/12.html
-----
The more unintuitive thing about shell is that what you wrote is technically better as:
() doesn't mean "grouping" like in other languages, it means subshell. {} means grouping, and it has an odd way of parsing, where ; is required before }, but it isn't required before ). This is because () are operators and {} aren't.I wrote about this here, "the subshell construct () is confused with the grouping construct { }":
http://www.oilshell.org/blog/2017/02/06.html
-----
EDIT: I will say that I was very confused by the syntax of > and < when I started learning shell. It does seem like ">" means "this goes into that". In fact I think I internalized that from MS-DOS.
But that doesn't explain what these mean, and why they are equivalent:
They are best read as prefix operators that modify the state of the process invocation.What's even more confusing is that you can do:
Then they look like binary operators again. But the descriptor is "stuck on", it's actually part of the operator lexically.Valid:
Invalid: In other words, there can't be any space between the LHS file descriptor and the > or <.The syntax of shell is horrible, and that's something I would like to fix with Oil shell. But I don't see the problem you are pointing out.