bash 仅对用户输入的第一个字符读取超时
bash read timeout only for the first character entered by the user
我已经搜索了一个简单的解决方案,它将读取具有以下功能的用户输入:
- 10 秒后超时,如果根本没有用户输入
- 如果第一个字符是在前 10 秒内输入的,则用户有无限的时间来完成他的回答。
我在 上找到了类似请求的解决方案(每次输入字符后超时)。尽管如此,这并不是我一直在寻找的功能,所以我开发了一个两行解决方案,如下所示:
read -N 1 -t 10 -p "What is your name? > " a
[ "$a" != "" ] && read b && echo "Your name is $a$b" || echo "(timeout)"
如果用户在输入第一个字符之前等待 10 秒,输出将是:
What is your name? > (timeout)
如果用户在 10 秒内键入第一个字符,则他有无限的时间来完成此任务。输出将如下所示:
What is your name? > Oliver
Your name is Oliver
但是,有以下警告:第一个字符一旦输入就不可编辑,而所有其他字符都可以编辑(退格并重新输入)。
您对解决警告有任何想法,或者您对请求的行为有其他简单的解决方案吗?
启用readline
并添加$a
作为第二次读取的默认值。
# read one letter, but don't show it
read -s -N 1 -t 10 -p "What is your name? > " a
if [ -n "$a" ]; then
# Now supply the first letter and let the user type
# the rest at their leisure.
read -ei "$a" b && echo "Your name is $b"
else
echo "(timeout)"
fi
这仍然会在回答第一个字母后显示第二个提示,但我认为没有更好的方法来处理这个问题;无法 "cancel" 超时 read
。理想的解决方案是使用一些命令 other 而不是 read
,但你必须自己编写它(可能作为 C 中的可加载内置命令)。
这个解决方案也许可以。
read -n1 -t 10 -p "Enter Name : " name && echo -en "\r" &&
read -e -i "$name" -p "Enter Name : " name || echo "(timeout)"
注意: 第二个 read
使用从第一个(-i
选项)捕获的文本来提供可编辑的缓冲区。回车return和相同的提示给用户的印象是他输入了相同的值。
测试条件:
GNU bash,版本 4.4.19(1)-发布
Ubuntu 18.04.2 LTS
我创建了一个函数来解决您关于第一个字母不可编辑的警告,如下所示。我只在我的本地 linux 服务器上测试过它,并且我没有假设它可以在其他地方或 BASH 的 newer/older 版本上工作(或者就此阅读,但我无法告诉我是什么版本 运行)
__readInput(){
str="What is your name? > "
tput sc # Save current cursor position
printf "$str"
read -n 1 -t 10 a # Wait 10 seconds for first letter
[[ $? -eq 0 ]] || return 1 # Return ErrorCode "1" if timed_out
while :; do # Infinite Loop
tput rc # Return cursor to saved position
printf "$str$a" # Print string (including what is saved of the user input)
read -n 1 b # Wait for next character
if [[ $? -eq 0 ]]; then
# We had proper user input
if [[ ${#b} -eq 0 ]]; then
# User hit [ENTER]
n=$a$b
break # End our loop
fi
rg="[A-Za-z-]" # REGEX for checking user input... CAVEAT, see below
if ! [[ $b =~ $rg ]] ;then
# We have an unrecognisied character return, assume backspace
[[ ${#a} -gt 0 ]]&&a=${a:0:(-1)} # Strip last character from string
tput rc # Return cursor to saved position
printf "$str$a " # This removes the ^? that READ echoes on backspace
continue # Continue our loop
fi
a=$a$b # Append character to user input
fi
done
}
您可以像下面这样调用这个函数:
declare n=""
__readInput
if [[ $? -eq 0 ]] || [[ ${#n} -eq 0 ]] ;then
echo "Your name is $n"
else
echo "I'm sorry, I didn't quite catch your name!"
fi
以上说明的注意事项
所以,你有一个我修复的警告,也许你(或我们的朋友)可以修复这个。输入的任何未包含在 $rg
REGEX 变量中的字符都将被视为 BACKSPACE。这意味着您的用户可以点击 F7
、=
、\
或 $rg
中指定字符以外的任何字符,并且 将被视为退格键
简答:
在第一个读取命令上添加 -s
选项,在第二个读取命令上添加 -ei
选项:
read -s -N 1 -t 10 -p "What is your name? > " a
[ "$a" != "" ] && read -ei "$a" b && echo "Your name is $b" || echo "(timeout)"
或者更好地处理空输入:
read -s -N 1 -t 10 -p "What is your name? > " a || echo "(timeout)" \
&& [ -n "$a" ] && read -ei "$a" b || echo \
&& echo "Your name is \"$b\""
详细回答:
在@chepner 的回答(感谢 -ei
选项!)和@paul-hodges 的评论的帮助下,这使我找到了一篇推广 -s
阅读选项的文章,我能够创建一个与我原来的 2-liner 非常相似的工作解决方案:
read -N 1 -t 10 -s -p "What is your name? > " a
[ "$a" != "" ] && read -ei "$a" b && echo "Your name is $b" || echo "(timeout)"
有些人可能喜欢相同功能的更精细的版本:
if read -N 1 -t 10 -s -p "What is your name? " FIRST_CHARACTER; then
read -ei "$FIRST_CHARACTER" FULL_NAME
echo "Your name is $FULL_NAME"
else
echo "(timeout)"
fi
解释:
- 第一个读取命令中的
-s
选项将确保 FIRST_CHARACTER 在键入时不会被打印出来。
-N 1
或 -n1
选项将确保只有第一个字符被读入 FIRST_CHARACTER 变量
- 在用户继续写入字符 2 到 n 之前,
-ei
选项会将 $FIRST_CHARACTER
读入 FULL_NAME。
- 用户可以重新考虑他的答案,他可以删除整个输入,包括带有退格键的第一个字符。
我已经测试过了,这些选项的组合似乎可以解决问题。
解决输入为空的警告
但是,仍然有一个小警告:如果用户只是键入 <enter>
:第二个读取命令将等待输入,直到用户再次按下 <enter>
。这可以像下面这样修复:
if read -N 1 -t 10 -s -p "What is your name? " FIRST_CHARACTER; then
if [ -n "$FIRST_CHARACTER" ]; then
read -ei "$FIRST_CHARACTER" FULL_NAME
else
echo
fi
echo "Your name is \"$FULL_NAME\""
else
echo "(timeout)"
fi
在两行样式中,这将为我们提供如下三行样式:
read -N 1 -t 10 -s -p "What is your name? > " a || echo "(timeout)" \
&& [ -n "$a" ] && read -ei "$a" b || echo \
&& echo "Your name is \"$b\""
测试
两个版本的代码(嵌套 if 版本和三行代码)的行为如下:
- 如果用户在 10 秒内没有做任何事情,输出将产生
What is your name? (timeout)
- 如果用户写入
Oliver<enter>
输出将是
What is your name? Oliver
Your name is "Oliver"
- 如果用户开始写"Oliver",然后考虑,他想被称为"Michael",他可以用退格键完全删除"Oliver"并相应地替换它.输出将是:
What is your name? Oliver
输入名字后"Oliver"。然后,按退格键 6 次或更多次后:
What is your name?
输入Michael<enter>
后:
What is your name? Michael
Your name is "Michael"
希望对您有所帮助。
我已经搜索了一个简单的解决方案,它将读取具有以下功能的用户输入:
- 10 秒后超时,如果根本没有用户输入
- 如果第一个字符是在前 10 秒内输入的,则用户有无限的时间来完成他的回答。
我在
read -N 1 -t 10 -p "What is your name? > " a
[ "$a" != "" ] && read b && echo "Your name is $a$b" || echo "(timeout)"
如果用户在输入第一个字符之前等待 10 秒,输出将是:
What is your name? > (timeout)
如果用户在 10 秒内键入第一个字符,则他有无限的时间来完成此任务。输出将如下所示:
What is your name? > Oliver
Your name is Oliver
但是,有以下警告:第一个字符一旦输入就不可编辑,而所有其他字符都可以编辑(退格并重新输入)。
您对解决警告有任何想法,或者您对请求的行为有其他简单的解决方案吗?
启用readline
并添加$a
作为第二次读取的默认值。
# read one letter, but don't show it
read -s -N 1 -t 10 -p "What is your name? > " a
if [ -n "$a" ]; then
# Now supply the first letter and let the user type
# the rest at their leisure.
read -ei "$a" b && echo "Your name is $b"
else
echo "(timeout)"
fi
这仍然会在回答第一个字母后显示第二个提示,但我认为没有更好的方法来处理这个问题;无法 "cancel" 超时 read
。理想的解决方案是使用一些命令 other 而不是 read
,但你必须自己编写它(可能作为 C 中的可加载内置命令)。
这个解决方案也许可以。
read -n1 -t 10 -p "Enter Name : " name && echo -en "\r" &&
read -e -i "$name" -p "Enter Name : " name || echo "(timeout)"
注意: 第二个 read
使用从第一个(-i
选项)捕获的文本来提供可编辑的缓冲区。回车return和相同的提示给用户的印象是他输入了相同的值。
测试条件: GNU bash,版本 4.4.19(1)-发布 Ubuntu 18.04.2 LTS
我创建了一个函数来解决您关于第一个字母不可编辑的警告,如下所示。我只在我的本地 linux 服务器上测试过它,并且我没有假设它可以在其他地方或 BASH 的 newer/older 版本上工作(或者就此阅读,但我无法告诉我是什么版本 运行)
__readInput(){
str="What is your name? > "
tput sc # Save current cursor position
printf "$str"
read -n 1 -t 10 a # Wait 10 seconds for first letter
[[ $? -eq 0 ]] || return 1 # Return ErrorCode "1" if timed_out
while :; do # Infinite Loop
tput rc # Return cursor to saved position
printf "$str$a" # Print string (including what is saved of the user input)
read -n 1 b # Wait for next character
if [[ $? -eq 0 ]]; then
# We had proper user input
if [[ ${#b} -eq 0 ]]; then
# User hit [ENTER]
n=$a$b
break # End our loop
fi
rg="[A-Za-z-]" # REGEX for checking user input... CAVEAT, see below
if ! [[ $b =~ $rg ]] ;then
# We have an unrecognisied character return, assume backspace
[[ ${#a} -gt 0 ]]&&a=${a:0:(-1)} # Strip last character from string
tput rc # Return cursor to saved position
printf "$str$a " # This removes the ^? that READ echoes on backspace
continue # Continue our loop
fi
a=$a$b # Append character to user input
fi
done
}
您可以像下面这样调用这个函数:
declare n=""
__readInput
if [[ $? -eq 0 ]] || [[ ${#n} -eq 0 ]] ;then
echo "Your name is $n"
else
echo "I'm sorry, I didn't quite catch your name!"
fi
以上说明的注意事项
所以,你有一个我修复的警告,也许你(或我们的朋友)可以修复这个。输入的任何未包含在 $rg
REGEX 变量中的字符都将被视为 BACKSPACE。这意味着您的用户可以点击 F7
、=
、\
或 $rg
中指定字符以外的任何字符,并且 将被视为退格键
简答:
在第一个读取命令上添加 -s
选项,在第二个读取命令上添加 -ei
选项:
read -s -N 1 -t 10 -p "What is your name? > " a
[ "$a" != "" ] && read -ei "$a" b && echo "Your name is $b" || echo "(timeout)"
或者更好地处理空输入:
read -s -N 1 -t 10 -p "What is your name? > " a || echo "(timeout)" \
&& [ -n "$a" ] && read -ei "$a" b || echo \
&& echo "Your name is \"$b\""
详细回答:
在@chepner 的回答(感谢 -ei
选项!)和@paul-hodges 的评论的帮助下,这使我找到了一篇推广 -s
阅读选项的文章,我能够创建一个与我原来的 2-liner 非常相似的工作解决方案:
read -N 1 -t 10 -s -p "What is your name? > " a
[ "$a" != "" ] && read -ei "$a" b && echo "Your name is $b" || echo "(timeout)"
有些人可能喜欢相同功能的更精细的版本:
if read -N 1 -t 10 -s -p "What is your name? " FIRST_CHARACTER; then
read -ei "$FIRST_CHARACTER" FULL_NAME
echo "Your name is $FULL_NAME"
else
echo "(timeout)"
fi
解释:
- 第一个读取命令中的
-s
选项将确保 FIRST_CHARACTER 在键入时不会被打印出来。 -N 1
或-n1
选项将确保只有第一个字符被读入 FIRST_CHARACTER 变量- 在用户继续写入字符 2 到 n 之前,
-ei
选项会将$FIRST_CHARACTER
读入 FULL_NAME。 - 用户可以重新考虑他的答案,他可以删除整个输入,包括带有退格键的第一个字符。
我已经测试过了,这些选项的组合似乎可以解决问题。
解决输入为空的警告
但是,仍然有一个小警告:如果用户只是键入 <enter>
:第二个读取命令将等待输入,直到用户再次按下 <enter>
。这可以像下面这样修复:
if read -N 1 -t 10 -s -p "What is your name? " FIRST_CHARACTER; then
if [ -n "$FIRST_CHARACTER" ]; then
read -ei "$FIRST_CHARACTER" FULL_NAME
else
echo
fi
echo "Your name is \"$FULL_NAME\""
else
echo "(timeout)"
fi
在两行样式中,这将为我们提供如下三行样式:
read -N 1 -t 10 -s -p "What is your name? > " a || echo "(timeout)" \
&& [ -n "$a" ] && read -ei "$a" b || echo \
&& echo "Your name is \"$b\""
测试
两个版本的代码(嵌套 if 版本和三行代码)的行为如下:
- 如果用户在 10 秒内没有做任何事情,输出将产生
What is your name? (timeout)
- 如果用户写入
Oliver<enter>
输出将是
What is your name? Oliver
Your name is "Oliver"
- 如果用户开始写"Oliver",然后考虑,他想被称为"Michael",他可以用退格键完全删除"Oliver"并相应地替换它.输出将是:
What is your name? Oliver
输入名字后"Oliver"。然后,按退格键 6 次或更多次后:
What is your name?
输入Michael<enter>
后:
What is your name? Michael
Your name is "Michael"
希望对您有所帮助。