I’m struggling to understand how you don’t get these features with tcl / tclsh.
You get job control, familiar access to your file system and the usual conventions around command execution of binaries in your PATH (like ps, kill, etc), you have stderr, you have pipes...
...but you also have a language that is immediately polyglot because of the way it uses strings. Each string reference is analogous to a file-descriptor; a function pointer that can be redirected and executed in whatever context, while also feeling like native context scopes in any languages you might want to use to interpret/compile them due to their use of the {} delimiter.
But you can't connect programs together via pipes from within TCL, except by doing a terrible hand-rolled implementation that does half the things that bash gives you for free. And arguably, the ability to connect programs together like that is the most powerful feature of any modern shell.
`open |myProgram` returns a file-descriptor for a pipe (that you can explicitly open for read, write, or read/write), and `exec ls |grep {myPattern}` will pipe the output of ls into grep...okay nevermind I do see your point.
p1 | p2 | p3 > file
is certainly nicer for interactive use, although I would argue that a hand-rolled pipe operator to give you the same in tcl comes with some pleasant fp benefits, and it’s only a few lines to define it to get the above, or supply alternative syntax like
| {p1} {p2} {p3} {[open file w]}
or if you want to also define the redirect,
| {p1} {p2} {p3} {> file}
where each portion of the pipe can be much more clearly manipulated in-stream, and with whatever interpreter, before returning the pipeable.
This gets especially nice for gluing when you want to run scripts in multiple interpreters but they are short and you want to maintain the context for readability between multiple execution contexts:
You get job control, familiar access to your file system and the usual conventions around command execution of binaries in your PATH (like ps, kill, etc), you have stderr, you have pipes...
...but you also have a language that is immediately polyglot because of the way it uses strings. Each string reference is analogous to a file-descriptor; a function pointer that can be redirected and executed in whatever context, while also feeling like native context scopes in any languages you might want to use to interpret/compile them due to their use of the {} delimiter.