【问题标题】:bash read timeout only for the first character entered by the userbash 仅针对用户输入的第一个字符读取超时
【发布时间】:2019-09-08 20:41:31
【问题描述】:

我已经搜索了一个简单的解决方案,它可以读取具有以下功能的用户输入:

  • 10 秒后超时,如果根本没有用户输入
  • 如果在前 10 秒内输入了第一个字符,则用户有无限的时间来完成他的回答。

我在Linux Read - Timeout after x seconds *idle* 上找到了类似请求(每个键入字符后超时)的解决方案。不过,这并不是我一直在寻找的功能,所以我开发了一个两行解决方案,如下所示:

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

但是,有以下警告:第一个字符一旦输入就不可编辑,而所有其他字符都可以编辑(退格和重新输入)。

您对解决警告有任何想法,或者您对请求的行为有其他简单的解决方案吗?

【问题讨论】:

  • 我很确定所有这些问题都可以解决,但是处理每个问题的代码,尤其是 all,并不简单。您可能会在 this slightly related article 中找到一些有用的提示。
  • @paul-hodges:可以通过在第一个读取命令上添加-s 选项和在第二个读取命令上添加-ei 选项来解决问题。更多信息请参见下面@chepner 的分析器以及下面更详细的答案。感谢您的链接,它使我们找到了-s 选项。因此,我将您的评论标记为有帮助。

标签: bash timeout user-input


【解决方案1】:

启用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

这仍然会在第一个字母被回答后显示第二个提示,但我认为没有更好的方法来处理这个问题;没有办法“取消”read 的超时。理想的解决方案是使用read 以外的一些命令other,但您必须自己编写(可能作为可加载的内置内置程序,用 C 语言)。

【讨论】:

  • 我测试了你的解决方案,有一个奇怪的效果:第一个字符加倍。并且警告仍然存在,即不能删除双倍字符中的第一个。例如。我输入“Oliver”,然后我会看到“OOliver”。我用了 10 次退格,但第一个 O 不能删除。如果我然后输入“Michael”,我会得到“OMichael”。
  • 答案对我仍然有帮助,因为我已经了解了有用的 -ei 阅读选项。因此,我会按相应的三角形。
  • 哦,对不起。 b 本身就是您想要的名称,因为 a 现在仅用于初始化 read 的输入:echo "Your name is $b"。
  • 嗨@chepner,这仍然不能解决问题。如果您键入“Oliver”,O 仍然是重复的,并且无法删除最初的“O”:What is your name? > OOliver。不过,在您更正之后,程序会输入Your name is Oliver。我已将您的答案标记为有帮助,因为 -ei 选项对于创建完整的解决方案非常有帮助。
  • 您只需要在第一个读取命令中添加一个-s 选项。如果你这样做,我会接受你的答案,即使我已经创建了自己的答案,我担心这有点过于复杂了。 ;-)
【解决方案2】:

这个解决方案可以。

read -n1 -t 10 -p "Enter Name : " name && echo -en "\r" &&
read -e -i "$name" -p "Enter Name : " name || echo "(timeout)"

注意:第二个read 使用从第一个(-i 选项)捕获的文本来提供可编辑的缓冲区。回车和相同的提示给用户一种他正在输入相同值的印象。

【讨论】:

  • 嗨sjsam,这是一个有趣的答案,也很有效(因此,+1)。而不是使用-s(静默)选项并且不打印最先读取的字符,您只需返回到与echo -en "\r" 相同的行的字符1 并重新编写相同的提示“输入名称:”第二次.
  • @Olli 没错!第一个echo是这里的关键。
【解决方案3】:

测试条件: GNU bash,版本 4.4.19(1)-release Ubuntu 18.04.2 LTS

我创建了一个函数来解决您对第一个字母不可编辑的警告,如下所示。我只用我的本地 linux 服务器对此进行了测试,并且我不假设这将在其他地方或新/旧版本的 BASH 上工作(或为此阅读,但我无法确定我正在运行的版本)

__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 变量中的字符都将被视为退格。这意味着您的用户可以点击F7、=、\ 或$rg 中指定的字符以外的任何字符,并且它将被视为退格

【讨论】:

  • 我还应该提到,目前,未经编辑,$rg 变量允许所有大小写字母以及连字符。
【解决方案4】:

简答:

在第一个读取命令上添加一个-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 变量
  • -ei 选项将在用户继续将字符 2 写入 n 之前将 $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

在输入名字“奥利弗”之后。然后,按退格键 6 次或更多次后:

What is your name?

然后输入Michael<enter>:

What is your name? Michael
Your name is "Michael"

希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    相关资源
    最近更新 更多