如何在zsh脚本中提示yes/no样式的确认?

46
使用zsh,我正在尝试在我的~/.zprofile中加入一个步骤,其中我可以交互式地询问一个是/否的问题。起初,我尝试了这种bash风格的方法,但我看到了以下形式的错误: read: -p: no coprocess (我知道通常zsh语法与bash不同-我尝试用sh仿真命令-precede它-emulate -LR sh-但没有任何区别)。
这个页面暗示语法可能不同,因此根据这个页面和zsh手册的指导,我尝试了这个: read -q REPLY?"这是我想问的问题?" 这样做会出现以下形式的错误: /home/user/.zprofile:5: no matches found: REPLY?"This is the question I want to ask?" 如何在zsh中提出一个简单的是/否问题?理想情况下,该命令只需吞咽一个字符,无需按Enter/Return键,并且是“安全”的-即后续测试默认为否/假,除非输入“Y”或“y”。
4个回答

52

来自 zsh - read

如果第一个参数包含“?”,则该单词的其余部分将在shell为交互式时在标准错误流上用作提示。

您必须引用整个参数。

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

这将提示您使用REPLY返回按下的字符,并询问:这是我想要问的问题吗?如果不加引号,zsh会尝试将该参数作为文件名进行匹配。如果找不到匹配的文件名,则会显示no matches found

27

我添加这个答案是因为每当您想要向用户请求确认时,您也希望能够对其进行操作。以下是一个使用read -q(感谢其他答案!)提示用户并基于结果进行分支以执行您想要的操作的函数(在本例中,是Git操作):

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
}

1
它有效!谢谢!但是它是如何工作的?我没有看到任何关于选择测试的内容,以确定值是否实际包含Y/y。 - Tongfa
4
@Tongfa 这有点微妙。-q标志使read在输入Yy时返回0(成功)。该返回码在if中进行测试。请参阅代码中的链接以获取手册页。 - Ben

11
请参阅ZSH手册了解ZSH的read文档。尝试:
read REPLY\?"This is the question I want to ask?"

有没有一些方法可以与自动完成一起使用? - Adrian Lopez
@AdrianLopez 试试vared:https://superuser.com/questions/427645/better-way-to-read-a-line-of-user-input-in-zsh-e-g-with-zle - mckeed

3

我为此创建了两个实用程序脚本:

  • read-string.sh 从输入中读取字符串(需要用户按下回车键)
  • read-char.sh 从输入中读取单个字符(不需要按下回车键)

无论用户使用bash还是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"

感谢Olaf提供他的原始答案

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接