13

bashスクリプトで矢印キーを使用して、上/左矢印キーが押された場合に特定のコマンドセットを実行し、下/右矢印キーが押された場合に特定のセットを実行することは可能ですか?データの表示中に矢印キーを使用し、このスクリプトを使用してデータを読み取ることにより、ユーザーをすばやく切り替える方法を模索しています。

function main()  # The main function that controls the execution of all other functions
{
  mkdir -p ~/usertmp  # Make a new temporary user directory if it doesn't exist
  touch ~/last_seen_output.txt  # Create the output file if it doesn't exist
  cat /dev/null > ~/last_seen_output.txt  # Make sure that the output file is empty
  gather  # Call the "gather" function
  total=$((`wc -l ~/usertmp/user_list.txt|awk '{print $1}'`-1))  # Calculate the total amount of lines and subtract 1 from the result
  echo Current Time: `date +%s` > ~/last_seen_output.txt  # Print the current time to the output file for later reference
  echo "" > ~/last_seen_output.txt  # Print a blank line to the output file
    if [ $log -eq 1 ]
      then
        # If it is enabled, then delete the old backups to prevent errors
        while [ $line_number -le $total ]
          do

            line_number=$((line_number+1))  # Add 1 to the current line number
            calculate # Call the "calculate" function
            hms  # Call the "hms" function to convert the time in seconds to normal time
            log
        done
      else
        while [ $line_number -le $total ]
          do
            line_number=$((line_number+1))  # Add 1 to the current line number
            calculate # Call the "calculate" function
            hms  # Call the "hms" function to convert the time in seconds to normal time
            echo "Displaying, please hit enter to view the users one by one."
            read  # Wait for user input
            if [ "$log_while_displaying" ]
              then
                log
                display
              else
                display
            fi
        done
    fi
}

https://github.com/jbondhus/last-seen/blob/master/last-seen.shは完全なスクリプトです。

「ユーザー入力を待つ」とコメントされている読み取りコマンドは、Enterキーを押して次のユーザーに移動するコマンドです。基本的に、このスクリプトはユーザーと、各ユーザーがログインしてから経過した時間を一覧表示します。矢印キーを使用して、表示されている各ユーザーを切り替えようとしています。キー入力をcaseするためにcaseステートメントを使用できるかもしれないと思いました。繰り返しになりますが、これが可能かどうかはわかりません。そうでない場合、誰かがこれを行う別の方法を考えることができますか?

4

9 に答える 9

20

異常なコマンドなしで、矢印キーやその他のキーを読み取ることができます。read条件付きで2番目の呼び出しを追加する必要があります。

escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
    read -rsn2 mode # read 2 more chars
fi
case $mode in
    'q') echo QUITTING ; exit ;;
    '[A') echo UP ;;
    '[B') echo DN ;;
    '[D') echo LEFT ;;
    '[C') echo RIGHT ;;
    *) >&2 echo 'ERR bad input'; return ;;
esac
于 2017-09-29T03:02:04.573 に答える
18

前述のように、カーソルキーは3バイトを生成し、home/endなどのキーは4バイトを生成します。私がどこかで見た解決策は、最初の1文字のread()を、非常に短いタイムアウトで3回の後続の1文字の読み取りに続くようにすることでした。最も一般的なキーシーケンスは、次のように表示できます。

#!/bin/bash
for term in vt100 linux screen xterm
  { echo "$term:"
    infocmp -L1 $term|egrep 'key_(left|right|up|down|home|end)'
  }

また、/ etc / inputrcには、readlineマッピングを含むこれらの一部が含まれています。したがって、元の質問に答えて、私がハッキングしているbashメニューからの抜粋を次に示します。

while read -sN1 key # 1 char (not delimiter), silent
do
  # catch multi-char special key sequences
  read -sN1 -t 0.0001 k1
  read -sN1 -t 0.0001 k2
  read -sN1 -t 0.0001 k3
  key+=${k1}${k2}${k3}

  case "$key" in
    i|j|$'\e[A'|$'\e0A'|$'\e[D'|$'\e0D')  # cursor up, left: previous item
      ((cur > 1)) && ((cur--));;

    k|l|$'\e[B'|$'\e0B'|$'\e[C'|$'\e0C')  # cursor down, right: next item
      ((cur < $#-1)) && ((cur++));;

    $'\e[1~'|$'\e0H'|$'\e[H')  # home: first item
      cur=0;;

    $'\e[4~'|$'\e0F'|$'\e[F')  # end: last item
      ((cur=$#-1));;

    ' ')  # space: mark/unmark item
      array_contains ${cur} "${sel[@]}" && \
      sel=($(array_remove $cur "${sel[@]}")) \
      || sel+=($cur);;

    q|'') # q, carriage return: quit
      echo "${sel[@]}" && return;;
  esac                  

  draw_menu $cur "${#sel[@]}" "${sel[@]}" "$@" >/dev/tty
  cursor_up $#
done
于 2012-08-01T12:23:37.710 に答える
11

を使用read -n 1して1文字を読み取り、caseステートメントを使用して、キーに基づいて実行するアクションを選択できます。

問題は、矢印キーが複数の文字を出力し、シーケンス(およびその長さ)が端末ごとに異なることです。

たとえば、私が使用している端末では、右矢印はを出力します^[[CCtrl-を押すと、端末が出力するシーケンスを確認できますV Right ArrowPage Upおよびなどの他のカーソル制御キーについても同じことが言えますEnd

<代わりに、やのような1文字のキーを使用することをお勧めします>。スクリプトでそれらを処理する方がはるかに簡単です。

read -n 1 key

case "$key" in
    '<') go_left;;
    '>') go_right;;
esac
于 2012-05-21T05:44:59.950 に答える
11
# This will bind the arrow keys

while true
do
    read -r -sn1 t
    case $t in
        A) echo up ;;
        B) echo down ;;
        C) echo right ;;
        D) echo left ;;
    esac
done
于 2014-07-31T17:32:39.973 に答える
9

これが質問に直接答えるかどうかはわかりませんが、関連していると思います-私はそれらのコードがどこから来ているのかをさまよっていました、そして私はついに見つけました:

最初は読むのが少し難しいです。左矢印の場合は、「Key」列で「LEFT 4」をbash検索し、表示されるシーケンスについては、「[D 1b 5b44」と書かれている5番目の(「keymap」-「normal」)列を検索します。 -これは、このキーを表す3バイト(27、91、68)です。

スレッドを見つける本当に古いbashの矢印キーを読み取る方法は?--UNIXとLinuxのフォーラムは、押されたキーのキーコードをダンプする短いワンライナーを書くように私を刺激しました。基本的に、キーを押してからEnterキーを押し(終了をトリガーするため)、保存したものを出力するためにread使用します(最後にCtrl-Cを押してループを終了します)。hexdumpread

$ while true; do read -p?; echo -n $REPLY | hexdump -C; done
?^[[D     
00000000  1b 5b 44                                          |.[D| # left arrow
00000003
?^[[C
00000000  1b 5b 43                                          |.[C| # right arrow
00000003
?^[[1;2D
00000000  1b 5b 31 3b 32 44                                 |.[1;2D| # Shift+left arrow
00000006
?^[[1;2C
00000000  1b 5b 31 3b 32 43                                 |.[1;2C| # Shift+right arrow
00000006
?^C

したがって、矢印キーには3バイトが必要ですが、Shift +矢印キーには6バイトが必要です!ただし、これらのシーケンスはすべて0x1b(27)で始まるように見えるためread -n1、バイトを読み取る前に、この値をチェックすることができます。上記の表の「normal」列と「shift/NUM-Lock」列についても5b、マルチバイトシーケンスの2番目のバイトのままです。


編集:Linuxで押されたキーの端末コードをスキャンするはるかに簡単で適切な方法はshowkeyを使用することです:

$ showkey 
Couldn't get a file descriptor referring to the console

$ showkey -h
showkey version 1.15

usage: showkey [options...]

valid options are:

    -h --help   display this help text
    -a --ascii  display the decimal/octal/hex values of the keys
    -s --scancodes  display only the raw scan-codes
    -k --keycodes   display only the interpreted keycodes (default)

$ sudo showkey -a

Press any keys - Ctrl-D will terminate this program

^[[A     27 0033 0x1b
         91 0133 0x5b
         65 0101 0x41
^[[B     27 0033 0x1b
         91 0133 0x5b
         66 0102 0x42
^[[A     27 0033 0x1b
         91 0133 0x5b
         65 0101 0x41
^[[D     27 0033 0x1b
         91 0133 0x5b
         68 0104 0x44
^[[C     27 0033 0x1b
         91 0133 0x5b
         67 0103 0x43
^C       3 0003 0x03
^M       13 0015 0x0d
^D       4 0004 0x04
于 2013-05-03T04:46:30.327 に答える
5

答えを使っeMPee584て、私はあなたのために良い解決策を思いついたと思います。その出力は回答とほとんど同じですuser3229933が、シフトキーによってトリガーされることはなく、ほとんどの端末で機能します。

UP DOWN LEFTRIGHTHOMEとENDキーがあります。「q」を押して終了します。これのほとんどはeMPee584

のようなエラーが発生した場合は、「-sn1」を「-sN1」に変更する必要がある場合がありますillegal option n

#!/bin/bash

while read -sn1 key # 1 char (not delimiter), silent
do

  read -sn1 -t 0.0001 k1 # This grabs all three symbols 
  read -sn1 -t 0.0001 k2 # and puts them together
  read -sn1 -t 0.0001 k3 # so you can case their entire input.

   key+=${k1}${k2}${k3} 

  case "$key" in
    $'\e[A'|$'\e0A')  # up arrow
        ((cur > 1)) && ((cur--))
        echo up;;

    $'\e[D'|$'\e0D') # left arrow
        ((cur > 1)) && ((cur--))
        echo left;;

    $'\e[B'|$'\e0B')  # down arrow
        ((cur < $#-1)) && ((cur++))
        echo down;;

    $'\e[C'|$'\e0C')  # right arrow
        ((cur < $#-1)) && ((cur++))
        echo right;;

    $'\e[1~'|$'\e0H'|$'\e[H')  # home key:
        cur=0
        echo home;;

    $'\e[4~'|$'\e0F'|$'\e[F')  # end key:
        ((cur=$#-1))
        echo end;;

    q) # q: quit
        echo Bye!
        exit;;

   esac                  

done
于 2019-05-18T14:50:07.477 に答える
0

JellicleCatの答えを拡張するには:

#!/bin/bash
escape_char=$(printf "\u1b")
read -rsn1 mode # get 1 character
if [[ $mode == $escape_char ]]; then
    read -rsn4 -t 0.001 mode # read 2 more chars
fi
case $mode in
    '') echo escape ;;
    '[a') echo UP ;;
    '[b') echo DOWN ;;
    '[d') echo LEFT ;;
    '[c') echo RIGHT ;;
    '[A') echo up ;;
    '[B') echo down ;;
    '[D') echo left ;;
    '[C') echo right ;;
    '[2~') echo insert ;;
    '[7~') echo home ;;
    '[7$') echo HOME ;;
    '[8~') echo end ;;
    '[8$') echo END ;;
    '[3~') echo delete ;;
    '[3$') echo DELETE ;;
    '[11~') echo F1 ;;
    '[12~') echo F2 ;;
    '[13~') echo F3 ;;
    '[14~') echo F4 ;;
    '[15~') echo F5 ;;
    '[16~') echo Fx ;;
    '[17~') echo F6 ;;
    '[18~') echo F7 ;;
    '[19~') echo F8 ;;
    '[20~') echo F9 ;;
    '[21~') echo F10 ;;
    '[22~') echo Fy ;;
    '[23~') echo F11 ;;
    '[24~') echo F12 ;;
    '') echo backspace ;;
    *) echo $mode;;
esac
于 2020-04-28T14:59:35.480 に答える
0

上記の答えはどれも私にはうまくいきませんでした!私はこのスレッドの多くの回答、およびグーグルを介した他の検索から断片を取得する必要がありました。これを作るのに約1時間かかりました。

私はUbuntu20.04LTSを実行していますが、これは私にとってはうまくいきます(ただし、「ハック」する必要があったため、完全ではない可能性があります)。

waitkey() {
  local end=""
  local key=""

  echo
  echo "   Press ESC ... "

  while [ "$end" == "" ]; do
    read -rsn1 key
    case "$key" in
      $'\x1b')
        local k=""
        # I'm not sure why I have to do this if statement,
        # but without it, there are errors.  took forever
        # to figure out why 'read' would dump me outta the script
        if [ "$IFS" ]; then
          read -rsn1 -t 0.1 holder && k="$holder"
        else
          IFS=read -rsn1 -t 0.1 holder && k="$holder"
        fi 

        if [ "$k" == "[" ]; then
          read -rsn1 -t 0.1 holder && kk="$holder"

          ##############################
          # you put your arrow code here
          #
          # eg:
          #  case "$kk" in
          #    "A") echo "up arrow!" ;; # do something ...
          #  esac
          ##############################
        elif [ "$k" == "O" ]; then
          read -rsn1 -t 0.1 holder && kk="$holder"

          # I am honestly not knowing what this is for
        elif [ "$k" == "" ]; then
          end=1
        fi
    esac
  done
}
于 2020-07-03T19:24:03.773 に答える
0

Shiftキーを押したまま、Enterキーとスペースを入力できるMac互換バージョンをお探しの方は次のようになります。

#!/bin/bash

ESC=$'\033'
SHIFT=$'[1;2'
# distinguish between enter and space
IFS=''

while true; do
    read -rsn1 a
    # is the first character ESC?
    if [[ $ESC == $a ]]; then
        read -rsn2 b
        # does SHIFT start with the next two characters?
        if [[ $SHIFT == "$b"* ]]; then
            read -rsn3 c
        fi
    fi

    input=$a$b$c
    unset b c

    case $input in
        $ESC[A) echo UP ;;
        $ESC[B) echo DOWN ;;
        $ESC[C) echo RIGHT ;;
        $ESC[D) echo LEFT ;;

        $ESC$SHIFT'A') echo SHIFT UP ;;
        $ESC$SHIFT'B') echo SHIFT DOWN ;;
        $ESC$SHIFT'C') echo SHIFT RIGHT ;;
        $ESC$SHIFT'D') echo SHIFT LEFT ;;

        '') echo ENTER ;;
        ' ') echo SPACE ;;

        q) break ;;
    esac
done
于 2022-03-05T20:28:56.003 に答える