3

我有一个旧的 shell 脚本需要移动到 bash。此脚本打印某些活动的进度并等待用户的命令。如果用户在 15 秒内没有采取任何行动,屏幕将重新绘制新进度并重新启动计时器。这是我的问题:

我正在尝试使用read -t 15 myVar- 这样等待循环 15 秒后将重新启动。然而,有一个场景给我带来了一个问题:

  • 屏幕重绘和脚本等待输入(打印' Enter command:')
  • 用户输入foo但不按enter
  • 15 秒后屏幕再次重绘,脚本等待输入 - 注意,foo屏幕上的任何位置都没有显示(打印“ Enter command:”)
  • 用户输入bar并按下enter

此时变量$myVar持有' foobar'。

我需要什么?我正在寻找一种方法来查找用户键入的第一个字符串,以便在刷新状态后重新显示它。这样用户将看到: Enter command: foo

在 Solaris 上,我可以使用stty -pendin将输入保存到某种缓冲区中,并在刷新运行后stty pendin从缓冲区获取此输入并将其打印在屏幕上。

是否有与stty pendin功能等效的 Linux?或者,也许你知道我的问题的一些 bash 解决方案?

4

4 回答 4

2

我想一种方法是手动累积用户输入......使用readwith-n1以便它在每个字符之后返回,然后你可以将它添加到你的字符串中。为防止过度绘图,您必须计算 15 秒时钟上剩余的秒数......


附录:

对于您对空格/返回的评论-您基本上想使用没有所需字符的 IFS,例如空格

例子:

XIFS="${IFS}"  # backup IFS
IFS=$'\r'      # use a character your user is not likely to enter (or just the same but w/o the space)


# Enter will look like en empty string
$ read -n1 X; echo --; echo -n "${X}" | od -tx1

--
0000000


# space will be presented as a character
$ read -n1 X; echo --; echo -n "${X}" | od -tx1
 --
0000000 20
0000001


# after you are all done, you probably wantto restore the IFS
IFS="${XIFS}"
于 2012-05-17T14:53:57.757 回答
1

扩展@nhed 所说的内容,可能是这样的:

#!/bin/bash

limit=5

draw_screen () {
    clear;
    echo "Elapsed time: $SECONDS" # simulate progress indicator
    printf 'Enter command: %s' "$1"
}

increment=$limit
str=
end=0
while ! [ $end -eq 1 ] ; do
    draw_screen "$str"
    while [ $SECONDS -lt $limit ] && [ $end -eq 0 ] ; do
            c=
            IFS= read -t $limit -r -n 1 -d '' c
            if [ "$c" = $'\n' ] ; then
                    end=1
            fi
            str="${str}${c}"
    done
    let limit+=increment
done
str="${str%$'\n'}" # strip trailing newline

echo "input was: '$str'"

解决方案并不理想:

  • 您有时可能会在循环中间输入并弄乱输入
  • 你不能很好地编辑任何东西(但这可以通过更多的工作来解决)

但也许对你来说已经足够了。

于 2012-05-17T17:51:23.577 回答
1

如果你有 Bash 4:

read -p 'Enter something here: ' -r -t 15 -e -i "$myVar" myVar

-e打开对用户文本输入的 readline 支持。使用-i以下文本作为向用户显示的输入缓冲区的默认内容。在这种情况下,以下文本是您正在读入的变量的先前内容。

示范:

$ myVar='this is some text'    # simulate previous entry
$ read -p 'Enter something here: ' -r -t 15 -e -i "$myVar" myVar
Enter something here: this is some text[]

where[]代表光标。如果需要,用户将能够退格并更正先前的文本。

于 2012-05-18T02:00:58.380 回答
1

好的,我想我有解决方案。我接受了 nhed 的提议并做了一些工作:)

主代码打印一些状态并等待输入:

while :
do
    # print the progress on the screen
    echo -n "Enter command: "
    tput sc # save the cursor position
    echo -n "$tmpBuffer" # this is buffer which holds typed text (no 'ENTER' key yet)
    waitForUserInput
    read arguments <<< $(echo $mainBuffer) # this buffer is set when user presses 'ENTER'
    mainBuffer="" # don't forget to clear it after reading
    # now do the action requested in $arguments
done

函数 waitForUserInput 等待 10 秒等待按键。如果没有输入 - 退出,但已经输入的键被保存在缓冲区中。如果按键被按下,它会被解析(添加到缓冲区,或者在退格的情况下从缓冲区中删除)。On Enter 缓冲区被保存到另一个缓冲区,从中读取它以进行进一步处理:

function waitForUserInput {
    saveIFS=$IFS # save current IFS
    IFS="" # change IFS to empty string, so 'ENTER' key can be read

    while :
    do
        read -t10 -n1 char
        if (( $? == 0 ))
        then
            # user pressed something, so parse it
            case $char in
                $'\b')
                    # remove last char from string with sed or perl
                    # move cursor to saved position with 'tput rc'
                    echo -n "$tmpBuffer"
                    ;;
                "")
                    # empty string is 'ENTER'
                    # so copy tmpBuffer to mainBuffer
                    # clear tmpBuffer and return to main loop
                    IFS=$saveIFS
                    return 0
                    ;;
                 *)
                    # any other char - add it to buffer
                    tmpBuffer=$tmpBuffer$char
                    ;;
            esac
        else
            # timeout occured, so return to main function
            IFS=$saveIFS
            return 1
        fi
    done
}

感谢大家的帮助!

于 2012-05-21T05:48:39.863 回答