仅对用户输入的第一个字符进行bash读取超时

时间:2019-04-18 13:36:52

标签: bash timeout user-input

我正在寻找一种简单的解决方案,该解决方案将读取具有以下功能的用户输入:

  • 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

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

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

4 个答案:

答案 0 :(得分: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中的可加载内置程序)。

答案 1 :(得分:1)

此解决方案可以。

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

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

答案 2 :(得分:0)

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

我创建了一个函数来解决您对第一个字母不可编辑的警告,如下所示。我仅在本地linux服务器上进行了此测试,并且我不做任何假设,也可以在其他版本或BASH的较新/较旧版本中使用(对此也无法阅读,但我无法确定我运行的是哪个版本)

.container-map {
  width: 400px; /*set as much as you like */
}

.map-background , .map-search-results {
  display: block;
  height: 50%;
}

.map-background {
  padding: 15px; /* set as much as you want - to affect the height/position of .map-filter */
}

.map-filter {
  width: 200px;
  height: 100%; /* top/bottom padding of [.map-background] will create the height differential here */
}

您可以调用类似于以下内容的函数:

__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 REGEX变量中的字符都将被视为BACKSPACE。这意味着您的用户可以打$rgF7=,或者实际上是\中指定的字符以外的任何其他字符,并且它将被视为退格< / strong>

答案 3 :(得分:0)

简短答案:

在第一个读取命令上添加-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衬套非常相似的工作解决方案:

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\""

测试

两个版本的代码(如果版本嵌套,则为三行代码)的行为如下:

  • 如果用户在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"

希望有帮助。