3

bash に移動する必要がある古いシェル スクリプトがあります。このスクリプトは、いくつかのアクティビティの進行状況を出力し、ユーザーのコマンドを待ちます。ユーザーが 15 秒間何も操作しない場合、画面は新しい進行状況で再描画され、タイマーが再び開始されます。これが私の問題です:

私は使用しようとしていますread -t 15 myVar-この方法で、15秒の待機ループが再開されます。ただし、問題を引き起こすシナリオがあります。

  • 画面が再描画され、スクリプトが入力を待機します (「Enter command:」が出力されます)
  • ユーザーは入力しますfooが、押しませんenter
  • foo15 秒後、画面は再び再描画され、スクリプトは入力を待機します -画面のどこにも表示されないことに注意してください(「 Enter command:」が出力されます)。
  • ユーザーが入力barして押すenter

この時点で、変数$myVarは ' foobar' を保持しています。

私が必要なものは何?ユーザーが最初に入力した文字列を見つける方法を探しているので、ステータスを更新した後に再表示できます。このようにして、ユーザーは次のように表示されます。 Enter command: foo

Solaris ではstty -pendin、入力をある種のバッファーに保存するために使用でき、リフレッシュ実行後にバッファーstty pendinからこの入力を取得して画面に出力することができました。

機能に相当する Linux はありstty pendinますか? または、私の問題に対するbashの解決策を知っていますか?

4

4 に答える 4

2

1つの方法は、ユーザー入力を手動で蓄積することだと思います... withを使用read-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

OK、解決策があると思います。私は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 秒間待機します。何も入力しない場合 - 終了しますが、既に入力されたキーはバッファに保存されます。キーが押された場合、解析されます (バッファーに追加されるか、バックスペースの場合はバッファーから削除されます)。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 に答える
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[]

はカーソル[]を表します。ユーザーは、必要に応じて前のテキストをバックスペースして修正することができます。

于 2012-05-18T02:00:58.380 に答える
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 に答える