Open cipriancraciun opened 4 years ago
Usually when stdout is redirected, the user does not want a command prompt on stderr. I'm not likely to make that the default behaviour.
Take for example the following simple use-case where my-tool
asks for an input, does something, and prints out the result; however the user wants to catch that in a variable (as part of a larger script):
VAR="$( ./my-tool )"
At the moment, the user stares at a blank screen, because liner
writes the prompt to stdout
, but waits inputs from stdin
. After I end my input, the value of VAR
actually contains >> some-input \n some-output
(i.e. some-input
is what I entered, >>
is the prompter, and some-output
is the actual thing it should be in there.)
Similar examples could be:
dump-some-db | ./my-encryption-tool >./some-db.enc
At the moment liner
is broken even for these simple use-cases.
Now regarding my proposal of always using stderr
as input and output and check if that is an actual TTY, would work in all these cases, and I can't think of any draw-backs. (Except as said when stderr
is redirected to some file, but in that case, stdout
is most likely redirected too.)
Sorry, you can't use stderr
as input. It's output only. So your dump-some-db | ./my-liner-tool | ./new.db
example can only work if the tool has a non-interactive mode.
There may be a way to attach input to the current TTY (looked up from stderr) instead of using stdin, but that's outside of the scope of liner (which is trying to be both as simple and as cross-platform as possible)
...I thought I left the following comment earlier, but I must have forgotten to press the Comment button:
In addition to the above, your suggested change breaks anyone who has a workflow of "./my-liner-tool < command_list > output". Even if I wanted to make this change, I cannot change behaviour that wildly without also changing the package name (or major version number, same thing).
Sorry, you can't use stderr as input. It's output only. So your dump-some-db | ./my-liner-tool | ./new.db example can only work if the tool has a non-interactive mode.
Yes you can. I have already done this for my usage of liner
:
https://github.com/cipriancraciun/z-run/blob/master/sources/lib/input.go#L52-L58
The basic idea is this: if the process is attached to a TTY then, unless the process is part of a pipeline, all stdin
, stdout
and stderr
are actually dup
of /dev/tty
, and thus you can technically use any of them to read / write. In case it is part of a pipeline, then stderr
usually remains the only descriptor pointing to /dev/tty
, thus can be used for both reading and writing.
For example running lsof -p $$
(i.e. the open file descriptors for the current shell) yields:
bash 9534 ciprian 0u CHR 136,5 0t0 8 /dev/pts/5
bash 9534 ciprian 1u CHR 136,5 0t0 8 /dev/pts/5
bash 9534 ciprian 2u CHR 136,5 0t0 8 /dev/pts/5
There may be a way to attach input to the current TTY (looked up from stderr) instead of using stdin, but that's outside of the scope of liner (which is trying to be both as simple and as cross-platform as possible)
Yes, that can be achieved by opening /dev/tty
, which is tied to the TTY of the process group the current process is part of. However as you've noted if /dev/stderr
is not a TTY, then the user really doesn't want it to interact with the console, thus in such a case an error should be issued.
In addition to the above, your suggested change breaks anyone who has a workflow of "./my-liner-tool < command_list > output". Even if I wanted to make this change, I cannot change behaviour that wildly without also changing the package name (or major version number, same thing).
I agree that backward compatibility should be kept. Thus perhaps a new constructor could be added that takes the input / output streams to be used. (And perhaps a wrapper that supplies /dev/stderr
if it is a TTY or errors out.)
I agree that backward compatibility should be kept. Thus perhaps a new constructor could be added that takes the input / output streams to be used.
This seems like a reasonable suggestion.
Usually in almost all circumstances
/dev/sdterr
is a TTY; meanwhile/dev/stdin
and/or/dev/stdout
can be redirected to files or pipes. (In fact if/dev/stderr
is not a TTY, most likely the other two are inherited or poorly set-up, and this process isn't meant to handle the terminal.)At the moment
liner
checks if any ofstdin
orstdout
are redirected, and if so degrades to a "dumb" terminal:My proposal (which I could implement if accepted) is to either:
/dev/stderr
for both input and output and check that itself is a TTY;stdin
orstdout
seems to be redirected, check if perhapsstderr
is a TTY and use that;I currently use
liner
in such a scenario, and for the moment I replacesyscall.Stdin
/syscall.Stdout
withsyscall.Stderr
and it seems to do the trick. However this hack works in my limited case, but as said I'm willing to code this proposed feature.