Comment afficher les caractères de contrôle (^ C, ^ D, ^ [,…) différemment dans le shell

13

Lorsque vous tapez des caractères de contrôle dans le shell, ils s'affichent en utilisant ce qu'on appelle la "notation caret". Escape par exemple est écrit comme ^[en notation caret.

J'aime personnaliser ma coque bash pour la rendre cool. J'ai par exemple changé mon PS1et PS2devenir colorisé. Je veux maintenant que les personnages de contrôle obtiennent une apparence unique afin de les distinguer davantage des personnages normaux.

$ # Here I type CTRL-C to abort the command.
$ blahblah^C
          ^^ I want these two characters to be displayed differently

Existe-t-il un moyen de faire en sorte que mon shell surligne les caractères de contrôle différemment?

Est-il possible de les afficher en gras ou peut-être de les faire apparaître dans des couleurs différentes du texte normal?

J'utilise bash shell ici mais je n'ai pas marqué la question bashcar il existe peut-être une solution qui s'applique à de nombreux shells différents.

Notez que je ne sais pas à quel niveau la surbrillance des caractères de contrôle a lieu. J'ai d'abord pensé que c'était dans la coquille elle-même. Maintenant, j'ai entendu dire que c'est une ligne de lecture qui contrôle la façon dont les caractères de contrôle sont dans des coquilles comme bash . La question est donc désormais identifiée readlineet je cherche toujours des réponses.

wefwefa3
la source
pourquoi pensez-vous que la coque les met en évidence? car bashil readlinegère ce genre de choses, et pour la plupart des autres, c'est le pilote tty.
mikeserv
Je ne savais pas. C'est pourquoi j'ai écrit la note. C'était juste ma supposition parce que d'autres applications aiment viet lessmettent souvent en évidence les caractères de contrôle différemment du texte normal. Je vais modifier la question avec ces informations. Je vous remercie!
wefwefa3
ça a du sens. ce genre de choses est plus difficile pour un shell parce que c'est une sorte de deuxième priorité. avec les éditeurs, leur travail consiste à fournir une interface de fichier écran <>, mais le shell est un interpréteur de commandes pour un langage interprété en premier et un éditeur de lignes en second (ou pas du tout) .
mikeserv
Readline est utilisé par d'autres programmes, mais il fait également partie de bash: c'est une bibliothèque qui y est liée. Et les obus d'aujourd'hui, en particulier bash, font à peu près tout ce que vous pouvez imaginer.
alexis
Utilisez simplement zshce qui le fait hors de la boîte.
Stéphane Chazelas

Réponses:

20

Lorsque vous appuyez sur Ctrl+X, votre émulateur de terminal écrit l'octet 0x18 sur le côté maître de la paire de pseudo-terminaux.

Ce qui se passe ensuite dépend de la configuration de la discipline de ligne tty (un module logiciel dans le noyau qui se situe entre le côté maître (sous le contrôle de l'émulateur) et le côté esclave (avec lequel interagissent les applications exécutées dans le terminal)).

Une commande pour configurer cette discipline de ligne tty est la sttycommande.

Lorsque vous exécutez une application stupide comme catcelle-ci ne sait pas et ne se soucie pas de savoir si son stdin est un terminal ou non, le terminal est dans un mode canonique par défaut où la discipline de ligne tty implémente un éditeur de ligne brut .

Certaines applications interactives qui ont besoin de plus que cet éditeur de ligne brut modifient généralement ces paramètres au démarrage et les restaurent à la sortie. Les obus modernes, à leur demande, sont des exemples de telles applications. Ils implémentent leur propre éditeur de ligne plus avancé.

En règle générale, lorsque vous entrez dans une ligne de commande, le shell place la discipline de ligne tty dans ce mode, et lorsque vous appuyez sur Entrée pour exécuter la commande en cours, le shell restaure le mode tty normal (comme c'était le cas avant d'émettre l'invite).

Si vous exécutez la stty -acommande, vous verrez les paramètres actuels utilisés pour les applications stupides . Vous êtes susceptible de voir icanon, echoet les echoctlparamètres sont activés.

Cela signifie que:

  • icanon: cet éditeur de ligne brute est activé.
  • echo: les caractères que vous saisissez (que l'émulateur de terminal écrit sur le côté maître) sont renvoyés en écho (mis à disposition pour lecture par l'émulateur de terminal).
  • echoctl: au lieu d'être répercutés asis, les caractères de contrôle sont répercutés comme ^X.

Alors, disons que vous tapez A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Return.

Votre émulateur de terminal envoie: AB\bC\x18\b\r. La discipline de ligne fera écho : AB\b \bC^X\b \b\b \b\r\net une application qui lit l'entrée du côté esclave ( /dev/pts/x) lira AC\n.

Tout ce que l'application voit est AC\n, et uniquement lorsque votre presse Enterne peut donc avoir aucun contrôle sur la sortie ^X.

Vous remarquerez que pour l' écho , le premier ^H( ^?avec certains terminaux, voir le eraseréglage) a abouti à \b \bêtre renvoyé au terminal. C'est la séquence pour déplacer le curseur en arrière, écraser avec de l'espace, déplacer à nouveau le curseur, tandis que la seconde ^Ha \b \b\b \bpour effet d'effacer ces deux caractères ^et X.

Le ^X(0x18) lui-même était en cours de traduction vers ^et Xpour la sortie. Comme B, il ne s'est pas rendu à l'application, car nous l'avons supprimé avec Backspace.

\r(aka ^M) a été traduit en \r\n( ^M^J) pour l'écho et \n( ^J) pour l'application.

Alors, quelles sont nos options pour ces applications stupides :

  • désactiver echo( stty -echo). Cela change efficacement la façon dont les personnages de contrôle sont répercutés, en ... ne faisant rien en écho. Pas vraiment une solution.
  • désactiver echoctl. Cela change la façon dont les caractères de contrôle (autres que ^H, ^M... et tous les autres utilisés par l'éditeur de ligne) sont répercutés. Ils sont ensuite répercutés tels quels . Autrement dit, le caractère ESC est envoyé comme l' octet \e( ^[/ 0x1b) (qui est reconnu comme le début d'une séquence d'échappement par le terminal), ^Gvous envoyez un \a(un BEL, faisant bip de votre terminal) ... Pas une option .
  • désactiver l'éditeur de ligne brute ( stty -icanon). Pas vraiment une option car les applications brutes deviendraient beaucoup moins utilisables.
  • éditez le code du noyau pour changer le comportement de la discipline de ligne tty afin que l' écho d'un caractère de contrôle envoie \e[7m^X\e[mau lieu de juste ^X(ici, \e[7mpermet généralement la vidéo inversée dans la plupart des terminaux).

Une option pourrait être d'utiliser un wrapper comme rlwrapcelui-ci est un hack sale pour ajouter un éditeur de ligne sophistiqué aux applications stupides. Ce wrapper essaie en fait de remplacer les simples read()s du terminal par des appels à l'éditeur de ligne readline (qui modifient le mode de la discipline de ligne tty).

Pour aller encore plus loin, vous pouvez même essayer des solutions comme celle-ci qui détourne toutes les entrées du terminal pour passer par l'éditeur de ligne de zsh (qui se trouve mettre en surbrillance ^Xs en vidéo inverse) en s'appuyant sur la :execfonctionnalité de l'écran GNU .

Maintenant, pour les applications qui implémentent leur propre éditeur de ligne, c'est à eux de décider comment l' écho est fait. bashutilise readline pour ce qui ne prend pas en charge la personnalisation de l'écho des caractères de contrôle.

Pour zsh, voir:

info --index-search='highlighting, special characters' zsh

zshmet en surbrillance les caractères non imprimables par défaut. Vous pouvez personnaliser la mise en évidence avec par exemple:

zle_highlight=(special:fg=white,bg=red)

Pour surligner le blanc sur le rouge pour ces caractères spéciaux.

La représentation textuelle de ces caractères n'est cependant pas personnalisable.

Dans un lieu UTF-8, 0x18 sera rendu comme ^X, \u378, \U7fffffff(deux points de code unicode non affectés) comme <0378>, <7FFFFFFF>, \u200b(un caractère unicode non vraiment imprimable) comme <200B>.

\x80dans une locale iso8859-1 serait rendu comme ^�... etc.

Stéphane Chazelas
la source
3

J'ai généralement ce code dans mon fichier .bashrc:

function get_exit_status()
{
        local code=$?
        if [ $code -ne 0 ]
        then
                printf $'\001\033[31m\002'"($code)"$'\001\033[0m\002'" "
        fi
}

puis j'appelle cette fonction dans ma PS1

PS1='\u@\h \w $(get_exit_status)'

Avec cela, si vous appuyez sur ^ C, vous le verrez dans votre invite

I@mycomputer ~ ^C
I@mycomputer ~ (130)

Tous les codes d'état de sortie qui ne sont pas "0" seront invités.

à bout
la source
$? se développe pour quitter le statut dans la PS1, par exemple PS1 = '$? \ u @ \ h \ w'
teknopaul