web-dev-qa-db-fra.com

le script shell répond à la pression de la touche

J'ai un script Shell qui dit essentiellement quelque chose comme

while true; do
    read -r input
    if ["$input" = "a"]; then 
        echo "hello world"           
    fi
done

C'est très bien, et bien, mais je viens de réaliser que le fait d'appuyer sur ENTER présente un sérieux problème dans cette situation. Ce dont j'ai besoin, c'est que le script réponde lorsqu'une touche est enfoncée, sans avoir à appuyer sur Entrée.

Existe-t-il un moyen d'obtenir cette fonctionnalité dans un script Shell?

17
j0h
read -rsn1

Attendez-vous à une seule lettre (et n'attendez pas la soumission) et soyez silencieux (ne réécrivez pas cette lettre).

23
pacholik

donc l'extrait de travail final est le suivant:

#!/bin/bash

while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
    echo "hello world"
fi
done
11
Donald Derek

Une autre façon de le faire, de manière non bloquante (je ne sais pas si c'est ce que vous voulez). Vous pouvez utiliser stty pour régler le temps de lecture minimum à 0. (peu dangereux si stty sane n'est pas utilisé après)

stty -icanon time 0 min 0

Ensuite, exécutez simplement votre boucle comme d'habitude. Pas besoin de -r.

while true; do
    read input

    if ["$input" = "a"]; then 
        echo "hello world"           
    fi
done

IMPORTANT! Après avoir terminé sans bloquer, vous devez vous rappeler de remettre stty à la normale en utilisant

stty sane

Si vous ne le faites pas, vous ne pourrez rien voir sur le terminal et il semblera se bloquer.

Vous voudrez probablement inclure un piège pour ctrl-C comme si le script était fermé avant de revenir à la normale, vous ne pourrez plus voir quoi que ce soit que vous tapez et il semblera que le terminal a gelé.

trap control_c SIGINT

control_c()
{
    stty sane
}

P.S Aussi, vous voudrez peut-être mettre une instruction sleep dans votre script afin de ne pas utiliser tout votre CPU car cela fonctionnera en continu aussi vite que possible.

sleep 0.1

P.S.S Il semble que le problème de suspension ne se produise que lorsque j'avais utilisé -echo comme je le faisais, ce qui n'est probablement pas nécessaire. Je vais le laisser dans la réponse, car il est toujours bon de réinitialiser stty à sa valeur par défaut pour éviter de futurs problèmes. Vous pouvez utiliser -echo si vous ne voulez pas que ce que vous avez tapé apparaisse à l'écran.

4
user3442743

Vous pouvez utiliser cette fonction getkey:

getkey() {
    old_tty_settings=$(stty -g)   # Save old settings.
    stty -icanon
    Keypress=$(head -c1)
    stty "$old_tty_settings"      # Restore old settings.
}

Il désactive temporairement le "mode canonique" dans les paramètres du terminal (stty -icanon) Puis renvoie l'entrée de "head" (un shell intégré) avec l'option -c1 qui renvoie UN octet d'entrée standard. Si vous n'incluez pas le "stty -icanon" alors le script fait écho à la lettre de la touche enfoncée et attend ensuite RETOUR (pas ce que nous voulons). "Head" et "stty" sont des commandes intégrées de Shell. Il est important de sauvegarder et de restaurer les anciens paramètres du terminal une fois que vous avez appuyé sur la touche.

Ensuite, getkey () peut être utilisé en combinaison avec une instruction "case / esac" Pour une sélection interactive à partir d'une liste d'entrées: exemple:

case $Keypress in
   [Rr]*) Command response for "r" key ;;
   [Ww]*) Command response for "w" key ;;
   [Qq]*) Quit or escape command ;;  
esac

Cette combinaison getkey()/case-esac peut être utilisée pour rendre de nombreux scripts Shell interactifs. J'espère que ça aide.

2
Ian Forsyth

J'ai un moyen de le faire dans mon projet: https://sourceforge.net/p/playshell/code/ci/master/tree/source/keys.sh

Il lit une seule clé chaque fois que key_readonce est appelé. Pour les clés spéciales, une boucle d'analyse spéciale s'exécuterait pour pouvoir également les analyser.

C'est la partie cruciale de celui-ci:

if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
    KEY[0]=$K

    if [[ $K == $'\e' ]]; then
        if [[ BASH_VERSINFO -ge 4 ]]; then
            T=(-t 0.05)
        else
            T=(-t 1)
        fi

        if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
            case "$K" in
            \[)
                KEY[1]=$K

                local -i I=2

                while
                    read -rn 1 -d '' "${T[@]}" "${S[@]}" "KEY[$I]" && \
                    [[ ${KEY[I]} != [[:upper:]~] ]]
                do
                    (( ++I ))
                done
                ;;
            O)
                KEY[1]=$K
                read -rn 1 -d '' "${T[@]}" 'KEY[2]'
                ;;
            [[:print:]]|$'\t'|$'\e')
                KEY[1]=$K
                ;;
            *)
                __V1=$K
                ;;
            esac
        fi
    fi

    utils_implode KEY __V0
1
konsolebox