Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show read prompt even when fish is piped #9974

Open
koedx opened this issue Aug 23, 2023 · 3 comments
Open

Show read prompt even when fish is piped #9974

koedx opened this issue Aug 23, 2023 · 3 comments

Comments

@koedx
Copy link

koedx commented Aug 23, 2023

fish --version: 3.6.1 (on Android 10/Termux)

This is a question/feature request regarding the possibilty of using stderr to display prompt & query user input using read --prompt-str the way that bash's read builtin seems to do.

By way of a concrete use case, suppose ./script.fish uses $(read --prompt-str "enter $field value: ") to query user for username/pw values that the script uses to populate & send a post request thru curl, ultimately outputting some html to stdout.

The issue arises when any such script is connected to a pipe, ie.

./script.fish | sed '/blah/' | read -lz html_out 

which would allow the output of interest to be captured for further interactive processing.

The issue (which I'm sure is no surprise) is that when stdout is connected to a pipe (rather than a tty) fish's read builtin does not emit interactive prompts. I experimented w/ trying to redirect read's stdout to no avail, ie. $(read -P "enter $field: " > /dev/tty). I'm assuming this use case is why bash's read utilizes stderr for querying user input (so piping output doesn't break an "interactive" script so long as stdin is a tty).

To my mind, as long as stdin is connected to a tty, the expected "interactive" queries of a script should function. Is there any clever begin...end block/redirection wizardry that would allow an "interactive" query to be carried out on stderr?

If not, does a flag or a graceful "fallback" that allows read (given that read is the builtin/canonical way to solicit user input) to transact on stderr seem reasonable/worth implementing?

Thx for any guidance!

@koedx koedx changed the title if stdin is a tty read should prompt on stderr if stdout is being piped (bash prompts on stderr by default) if stdin is a tty read should prompt on stderr if stdout is being piped (bash's read prompts on stderr by default) Aug 23, 2023
@henrikhorluck
Copy link
Contributor

./script.fish | sed '/blah/' | read -lz html_out

In that example read's stdin is connected to the pipe, stdout is not. The way default way piping works is to redirect stdout of the previous entry to stdin in the next. See the docs. I think this might be a misunderstanding.

@faho
Copy link
Member

faho commented Aug 23, 2023

By way of a concrete use case, suppose ./script.fish uses $(read --prompt-str "enter $field value: ") to query user for input (specifically, username/pw to set values).

That script should use read --prompt-str "enter $field value: " value - let read directly set the variable, no need for the command substitution (which will just cause read to print its input again).

Is there any clever begin...end block/redirection wizardry that would allow an "interactive" query to be carried out on stderr?

No. Fundamentally, fish's read starts another instance of fish's interactive reader, and that's fairly coupled to fish's own stdout.

I've just tried experimenting with printing to stdin instead - since our "should we be interactive" checks are all based around isatty(STDIN) already.

I'm not sure if there are other cases where those two diverge, or where stdin isn't writable - typically terminals will just hand you three fds all pointing to the same pty, but what if you're in another setup like tmux?

I think this might be a misunderstanding.

@henrikhorluck The outside read has stdin connected to the pipe, the read inside of script.fish does not.

It's about the inside read writing its prompt into the pipe while querying for input on stdin (which is the terminal).

Here's the difference:

read --prompt-str="Input: " | read -lz foo

This has the first read read input from stdin, and, because conceptually fish knows it's read in a pipe (technically: it uses its stdoutput() outputter_t), it writes the prompt to its own stdout, ignoring the pipe. (it then engages read's mode without a variable, which is to just print the value - through the pipe. that's useful to demonstrate this, a script.fish would echo the value itself)

fish -c 'read --prompt-str="Input: "' | read -lz foo

This has the fish -c stdout redirected into the pipe. However, it will still see that stdin is a terminal, and engage read's interactive mode. You won't see a prompt, but you can type input and end it, and if you then echo $foo you will see the prompt in there.

My question is: If we check if stdin is a terminal to determine whether an interactive prompt for read is okay, then why do we print the prompt to another fd? We could use stderr, but that could also be redirected away - the only way to ensure that we print and read from the same terminal is to use the same fd.

(note: this is not the same as checking isatty(stdout) for determining whether the whole session is interactive, that's probably fine - or should even be augmented as isatty(stdout) && isatty(stdin))

@koedx
Copy link
Author

koedx commented Aug 23, 2023

That script should use read --prompt-str "enter $field value: " value - let read directly set the variable, no need for the command substitution (which will just cause read to print its input again).

The reason I used $(read...) is bcz in the script that particular read invocation is embedded in a string being used to compose the parameters to curl. ie.

set -l curl_args -a "--data-urlencode "$name=$(read --P "enter $name value: ")"

I know (inside the script) I can work around missing read prompts by echoing a prompt to stderr before calling read (ie. echo "enter $name value:" > &2; read...). The read call will then block while waiting for user input (user can then type "blind" & press enter allowing the script to receive input), but, as mentioned, the user can't see what they are typing (ie. no interactive "reader prompt" is displayed, which is precisely the issue I'm trying to solve).

I also realize that interactive read query "works" as long as inside script I output everything to stderr & then pipe stderr (ie. ./script.fish 2>| sed... | read -z captured), but that just seems... very ugly >_<.

@koedx koedx closed this as completed Aug 23, 2023
@faho faho reopened this Aug 23, 2023
@faho faho added this to the fish-future milestone Aug 23, 2023
@faho faho changed the title if stdin is a tty read should prompt on stderr if stdout is being piped (bash's read prompts on stderr by default) Show read prompt even when fish is piped Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants