Lire et traiter une string, char par char, tout en permettant à l'user de modifier l'input en ligne simple

Je veux lire de manière incrémentielle une ligne d'input du terminal, et permettre à l'user quelques fonctionnalités d'édition de ligne de base; INS, DEL, DROITE, GAUCHE HOME, END, BACKSPACE

A chaque fois que la string est modifiée, je veux la traiter, faire une search par regex incrémentale d'un file text.

Ces touches d'édition, et d'autres, génèrent plusieurs caractères d'input qui
rendre l'interprétation de l'input plutôt difficile, par exemple C-Left génère 6 caractères.

Existe-t-il un moyen simple d'get cette input modifiable char-par-char?
Je suis particulièrement intéressé à savoir comment faire cela en bash, car le rest du traitement sera bash .. D'autres suggestions sont les bienvenues aussi ..

Voici comment j'ai commencé, mais ça devient un peu incontrôlable avec une telle variété potentielle de codes de contrôle.

#!/bin/bash IFS=$'\n' while true ;do read -n 1 c ((${#c}==0)) && break # Exit the loop. Input length is 0 # ie. The user has pressed Enter echo "xx=$(echo -n "$c"|xxd -p)=" # 1b 5b 32 7e "INS" # 1b 5b 33 7e "DEL" # 1b 5b 43 "RIGHT" # 1b 5b 44 "LEFT" # 1b 5b 46 "END" # 1b 5b 48 "HOME" # 7f "BACKSPACE" done 

Si vous lisez un caractère à la fois avec read -n , vous devrez implémenter un parsingur de séquence de keys. Vous pouvez créer une solution lente et sale qui fonctionne avec la plupart des terminaux: considérez qu'une séquence d'échappement de key de fonction commence par un caractère d'échappement et se poursuit avec un nombre quelconque de caractères entre 0-9;[]O suivi d'un dernier caractère pas dans cet set.

Une meilleure façon de lire les inputs est d'utiliser une bibliothèque d'input appropriée. Bash en utilise un pour ses propres besoins ( readline ). Vous obtenez une interface limitée en déclarant vos propres raccourcis keyboard avec le lien embedded; bind -x spécifiquement bind -x pour exécuter une command shell sur une touche. En raison de cette interface limitée, la mise en œuvre de ce que vous voulez est susceptible d'être possible mais difficile.

Zsh a sa propre bibliothèque d'input, zle . Son interface est beaucoup plus riche que celle de bash. Avec zle, vous pouvez définir des keymaps arbitraires, et vous obtenez plus d'access aux internes de zle à partir du code shell. Utilisez zle pour assigner des fonctions shell aux commands définies par l'user (appelées widgets), bindkey pour créer et remplir votre propre keymap, et enfin vared pour lire une ligne d'input en utilisant la keymap de votre choix.

Vous pouvez mettre le terminal en mode brut après la première read s'il y a un caractère esc , puis utiliser une seconde read pour lire et parsingr les octets restants s'il y en a (voir traumatisme des caractères d'échappement BASH et voir aussi flows.txt ).

 #!/bin/bash # tested on Mac OS X 10.6.8 IFS=$'\n' old_tty_settings="$(stty -g)" exec 0</dev/tty tput smir # enable insert mode while IFS="" read -r -s -n1 key; do # first read (reads only a single byte) #od -c <<<"$key" #continue ((${#key}==0)) && break case "$key" in $'\001') printf '\r' # ctrl-a continue;; $'\177') tput rmir printf "\010\040\010\033[P" # backspace tput smir continue;; $'\025') printf "\033[1K" # tput el1 does not work on Mac OS X 10.6.8 continue;; # ctrl-u (clear to start of line) $'\v') tput el continue;; # ctrl-k (clear to end of line) esac # if the first char is esc (ie \e or \033 respectively) if [[ "$key" == $'\033' ]]; then stty cbreak -echo min 0 time 0 # set raw terminal IFS="" read -r bytes # second read (reads remaining bytes) if [[ ${#bytes} -gt 0 ]]; then stty "$old_tty_settings" case "${key}${bytes}" in $'\033[3~') tput dch1 # delete one char continue;; esac printf "${key}${bytes}" else stty "$old_tty_settings" fi else #stty "$old_tty_settings" printf '%s' "$key" fi done echo exit 0 

Jetez un oeil sur le sélecteur d' outils de command line.

 # usage examples selector -v -x @ <(find . -maxdepth 2 -type d | awk '{print $0"@cd "$0}') selector -v -x @ <(grep -E -o 'http[^ ]+' fileWithURLS)