53

Using zsh, I'm trying to put a step in my ~/.zprofile where I interactively ask a yes/no style question. At first I tried this bash-style approach, but I saw errors of this form:

read: -p: no coprocess

(I'm aware that typically the zsh syntax is different from bash's - I tried preceding it with a sh emulation command - emulate -LR sh - but it made no difference).

This page implied the syntax might be different, so guided by this page and the zsh man page, I tried this instead:

read -q REPLY?"This is the question I want to ask?"

This instead fails with an error of the form:

/home/user/.zprofile:5: no matches found: REPLY?"This is the question I want to ask?"

How can I ask a simple yes/no question with zsh? Ideally the command would just swallow one character, with no need to press Enter/Return, and be 'safe' - i.e. the subsequent test defaults to no/false unless 'Y' or 'y' are entered.

4 Answers 4

60

From zsh - read

If the first argument contains a ‘?’, the remainder of this word is used as a prompt on standard error when the shell is interactive.

You must quote the entire argument

read -q "REPLY?This is the question I want to ask?" 

this will prompt you with This is the question I want to ask? and return the character pressed in REPLY.

If you don't quote the question mark, zsh tries to match the argument as a filename. And if it doesn't find any matching filename, it complains with no matches found.

Sign up to request clarification or add additional context in comments.

Comments

34

I'm adding this answer because every time you want to ask the user for confirmation, you also want to act on it. here's a function that prompts with read -q (thanks, other answers!) and branches on the result to do what you want (in this case, git stuff):

git_commit_and_pull() { # http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html#index-read if read -q "choice?Press Y/y to continue with commit and pull: "; then git add . && git commit -m 'haha git goes brrr' && git pull else echo echo "'$choice' not 'Y' or 'y'. Exiting..." fi } 

2 Comments

It works! Thank you! but how does it work? I don't see a test on choice to tell whether the value actually contains a Y/y.
@Tongfa It's subtle. the -q flag makes read return 0 (success) if the input is Y or y. That return code is tested in the if. See the link in code for the man page
13

See ZSH Manual for documentation of ZSH's read. Try:

read REPLY\?"This is the question I want to ask?" 

2 Comments

Some way to have it working with autocomplete?
@AdrianLopez Try vared: superuser.com/questions/427645/…
3

I created two utility scripts for this:

  • read-string.sh reads a string from input (requires user to press enter)
  • read-char.sh reads a single char from input (does not require pressing enter)

Both scripts work fine regardless of whether the user is using bash or zsh.

#!/bin/bash # read-string.sh # eg: my_string=$(./read-string.sh); echo "my_string: $my_string" # bash `read` manual - https://ss64.com/bash/read.html # # read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] # [-N nchars] [-p prompt] [-r] [-s] [-t timeout] [-u fd] # [name...] # # -r Do not treat a Backslash as an escape character. The backslash is considered to be part # of the line. In particular, a backslash-newline pair can not be used as a line continuation. # Without this option, any backslashes in the input will be discarded. # You should almost always use the -r option with read. # zsh `read` manual - http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html#index-read # # read [ -rszpqAclneE ] [ -t [ num ] ] [ -k [ num ] ] [ -d delim ] # [ -u n ] [ name[?prompt] ] [ name ... ] # # -r Raw mode: a ‘\’ at the end of a line does not signify line continuation and backslashes in the line # don’t quote the following character and are not removed. if [ -n "$ZSH_VERSION" ]; then read -r "answer?" else read -r answer fi echo "$answer" 
#!/bin/bash # eg: my_char=$(read-char.sh); echo "my_char: $my_char" # bash `read` manual - https://ss64.com/bash/read.html # # read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] # [-N nchars] [-p prompt] [-r] [-s] [-t timeout] [-u fd] # [name...] # # -r Do not treat a Backslash as an escape character. The backslash is considered to be part # of the line. In particular, a backslash-newline pair can not be used as a line continuation. # Without this option, any backslashes in the input will be discarded. # You should almost always use the -r option with read. # -n nchars read returns after reading nchars characters rather than waiting for a complete line of input. # zsh `read` manual - http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html#index-read # # read [ -rszpqAclneE ] [ -t [ num ] ] [ -k [ num ] ] [ -d delim ] # [ -u n ] [ name[?prompt] ] [ name ... ] # # -q Read only one character from the terminal and set name to ‘y’ if this character was ‘y’ or ‘Y’ # and to ‘n’ otherwise. With this flag set the return status is zero only if the character was ‘y’ or ‘Y’. # This option may be used with a timeout (see -t); if the read times out, or encounters end of file, # status 2 is returned. Input is read from the terminal unless one of -u or -p is present. # This option may also be used within zle widgets. # -r Raw mode: a ‘\’ at the end of a line does not signify line continuation and backslashes in the line # don’t quote the following character and are not removed. if [ -n "$ZSH_VERSION" ]; then read -r -q "answer?" else read -r -n 1 answer fi echo "$answer" 

Thanks to Olaf for his original answer.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.