Je veux lire une ligne de texte et pouvoir déplacer le curseur à gauche et à droite pendant l'édition.
Lorsque j'appelle cat
ou utilise read
dans bash et que j'appuie sur les touches fléchées, j'obtiens ^[[A^[[B^[[C^[[D
au lieu de déplacer le curseur.
J'utilise bash
de gnome-terminal
et mate-terminal
, localement (pas de SSH).
Grâce à @ steeldriver, j'ai trouvé une solution qui permet de déplacer le curseur vers la gauche/la droite et ne montre pas l'historique de bash lorsque vous appuyez sur les flèches haut/bas.
history -c # clear history
read -ep "$*" var # read value using readline,
# display Prompt supplied as argument
echo "$var" # echo the value so it can be captured by the caller
Appelez ensuite ce script à partir d'un autre script ou shell:
var=`readline 'value: '`
Cette fonction peut être définie pour s'exécuter dans un sous-shell, la rendant ainsi essentiellement identique au script ci-dessus:
readline() (
history -c
read -ep "$*" var
echo "$var"
)
Ou bien il peut être exécuté directement dans le shell actuel, auquel cas l'historique de ce dernier doit être sauvegardé avant de le vider, puis restauré:
readline() {
history -w # write current history to the $HISTFILE
history -c # ...
read -ep "$*" var # ... same as above
echo "$var" # ...
history -r # resotre history (read from $HISTFILE)
}
Cependant, si vous décidez d'appuyer sur Ctrl+C lors de la saisie de texte, vous vous retrouverez sans historique, car la fonction sera interrompue avant la restauration de l'historique.
La solution consiste à utiliser des pièges. Mettre en place un piège sur le signal INT qui restaure l’historique puis "extrait" le signal.
readline() {
# set up a trap which restores history and removes itself
trap "history -r; trap - SIGINT; return" SIGINT
history -w
history -c
read -ep "$*" var
echo "$var"
history -r
trap - SIGINT
}
Cependant, si une interruption est déjà configurée sur le signal INT, il vous suffira de l’écarter. Vous devez donc sauvegarder le piège déjà existant, puis en créer un nouveau, gérer votre entreprise, puis restaurer l’ancien.
readline() {
local err=0 sigint_trap orig_trap
sigint_trap=`trap -p | grep ' SIGINT$'`
if [[ $sigint_trap ]]; then
# A trap was already set up ‒ save it
orig_trap=`sed 's/trap -- \(.*\) SIGINT$/\1/' <<<"$sigint_trap"`
fi
# Don't do anything upon receiving SIGINT (eg. user pressed Ctrl+C).
# This is to prevent the function from exiting before it has restored
# the original trap.
trap ':' SIGINT
# `read` must be called from a subshell, otherwise it will run
# again and again when interrupted. This has something to do with
# the fact that `read` is a Shell builtin. Since `read` reads a value
# into variable in a subshell, this variable won't exist in the parent
# Shell. And since a subshell is already used, the history might as well
# be cleared in the subshell instead of the current Shell ‒ then it's
# not necessary to save and restore it. If this subshell returns a
# non-zero value, the call to `read` was interrupted, and there will be
# no output. However, no output does not indicate an interrupted read,
# since the input could have been empty. That's why an exit code is
# necessary ‒ to determine whether the read was interrupted.
( history -c
read -ep "$*" var
echo "$var"
) || {
# `read` was interrupted ‒ save the exit code and echo a newline
# to stderr (because stdin is captured by the caller).
err=$?
echo >&2
}
# The subshell can be replaced by a call to the above script:
## "`which readline`" "$@" || { err=$?; echo >&2; }
if [[ $sigint_trap ]]; then
# Restore the old trap
trap "`eval echo "$orig_trap"`" SIGINT
else
# Remove trap
trap - SIGINT
fi
# Return non-zero if interrupted, else zero
return $err
}
Ainsi, même si cette dernière version est "légèrement" plus complexe que la version d'origine et n'empêche toujours pas le démarrage d'un sous-shell, elle indique si la lecture a réussi ou non (ce qui n'est le cas pour aucune des versions plus simples).
Il peut être utilisé comme suit:
my_function() {
...
message=`readline $'\e[1mCommit message:\e[m '` || {
echo "[User abort]" >&2
return 1
}
...
}
Avec le read
intégré au shell bash
, vous pouvez utiliser l'option -e
pour activer la prise en charge de readline. De help read
:
-e use Readline to obtain the line in an interactive Shell
Par exemple
read -ep "Please enter some text: "
Je ne connais aucun moyen de faire cela avec un cat
ici-document.