Cómo mostrar los caracteres de control (^ C, ^ D, ^ [, ...) de manera diferente en el shell

13

Cuando escribe caracteres de control en el shell, se muestran utilizando lo que se denomina "notación de intercalación". Escape, por ejemplo, se escribe como ^[en notación de intercalación.

Me gusta personalizar mi bash shell para que se vea genial. Por ejemplo, he cambiado mi PS1y PS2para colorear. Ahora quiero que los personajes de control tengan una apariencia única también para que sean más distinguibles de los personajes normales.

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

¿Hay alguna manera de hacer que mi shell resalte los caracteres de control de manera diferente?

¿Es posible hacer que se muestre en negrita o tal vez hacer que aparezcan en diferentes colores del texto normal?

Estoy usando bash shell aquí, pero no etiqueté la pregunta bashporque quizás haya una solución que se aplique a muchos shells diferentes.

Tenga en cuenta que no sé en qué nivel tiene lugar el resaltado de los caracteres de control. Primero pensé que estaba en el caparazón mismo. Ahora he oído que es readline que controla cómo los personajes de control están en shells como bash . Así que la pregunta ahora está etiquetada readliney todavía estoy buscando respuestas.

wefwefa3
fuente
¿Por qué crees que el shell los está resaltando? porque bashestá readlinemanejando esas cosas, y para la mayoría de los demás es el controlador tty.
mikeserv
No lo sabia Por eso escribí la nota. Era sólo mi suposición porque otras aplicaciones como vi, y lesscon frecuencia los caracteres de control resaltado diferente del texto normal. Editaré la pregunta con esta información. ¡Gracias!
wefwefa3
eso tiene sentido. ese tipo de cosas es más difícil para un shell porque es una especie de segunda prioridad. con los editores, todo su trabajo es proporcionar una interfaz de archivo de pantalla <>, pero el shell es un intérprete de comandos para un lenguaje interpretado primero y un editor de línea en segundo lugar (o nada) .
mikeserv
Readline es utilizado por otros programas, pero también es parte de bash: es una biblioteca vinculada a él. Y los proyectiles de hoy, especialmente bash, hacen casi cualquier cosa que se te ocurra.
alexis
Simplemente use zshcuál lo hace fuera de la caja.
Stéphane Chazelas

Respuestas:

20

Cuando presiona Ctrl+X, su emulador de terminal escribe el byte 0x18 en el lado maestro del par pseudo-terminal.

Lo que sucede a continuación depende de cómo se configura la disciplina de línea tty (un módulo de software en el núcleo que se encuentra entre el lado maestro (bajo el control del emulador) y el lado esclavo (con el que interactúan las aplicaciones que se ejecutan en el terminal).

Un comando para configurar esa disciplina de línea tty es el sttycomando.

Cuando ejecuta una aplicación tonta como catesa no es consciente y no le importa si su stdin es una terminal o no, la terminal está en un modo canónico predeterminado donde la disciplina de línea tty implementa un editor de línea cruda .

Algunas aplicaciones interactivas que necesitan más que ese editor de línea cruda suelen cambiar esa configuración al inicio y restaurarla al salir. Los proyectiles modernos, a su solicitud, son ejemplos de tales aplicaciones. Implementan su propio editor de línea más avanzado.

Por lo general, mientras ingresa una línea de comando, el shell coloca la disciplina de línea tty en ese modo, y cuando presiona enter para ejecutar el comando actual, el shell restaura el modo tty normal (como estaba vigente antes de emitir el aviso).

Si ejecuta el stty -acomando, verá la configuración actual en uso para las aplicaciones tontas . Es probable que vea el icanon, echoy echoctllos ajustes que se habilitó.

Lo que eso significa es que:

  • icanon: ese editor de líneas crudas está habilitado.
  • echo: los caracteres que escribe (que el emulador de terminal escribe en el lado maestro) se repiten (están disponibles para su lectura por el emulador de terminal).
  • echoctl: en lugar de repetirse como asis, los caracteres de control se repiten como ^X.

Entonces, digamos que escribes A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Return.

El emulador de terminal enviará: AB\bC\x18\b\r. La disciplina de línea hará eco : AB\b \bC^X\b \b\b \b\r\ny se leerá una aplicación que lee la entrada del lado esclavo ( /dev/pts/x) AC\n.

Todo lo que ve la aplicación es AC\n, y solo cuando presiona Enterpara que no pueda tener ningún control en la salida ^Xallí.

Notarás que para echo , el primero ^H( ^?con algunos terminales, ver la eraseconfiguración) resultó en \b \bser enviado de vuelta al terminal. Esa es la secuencia para mover el cursor hacia atrás, sobrescribir con espacio, mover el cursor hacia atrás nuevamente, mientras que el segundo ^Hresultó en \b \b\b \bborrar esos dos ^y Xcaracteres.

El ^X(0x18) en sí mismo se estaba traduciendo hacia ^y Xpara la salida. Al igual B, no llegó a la aplicación, ya que la eliminamos con Retroceso.

\r(aka ^M) se tradujo a \r\n( ^M^J) para echo, y \n( ^J) para la aplicación.

Entonces, ¿cuáles son nuestras opciones para esas aplicaciones tontas ?

  • deshabilitar echo( stty -echo). Eso cambia efectivamente la forma en que se hacen eco de los caracteres de control, al ... no hacer eco de nada. Realmente no es una solución.
  • desactivar echoctl. Eso cambia la forma en que se hacen eco los caracteres de control (que no sean ^H, ^M... y todos los demás utilizados por el editor de línea). Luego se repiten tal cual. Es decir, por ejemplo, el carácter ESC se envía como el byte \e( ^[/ 0x1b) (que es reconocido como el inicio de una secuencia de escape por el terminal), ^Gusted envía un \a(un BEL, haciendo que su terminal emita un pitido) ... No es una opción .
  • deshabilitar el editor de línea cruda ( stty -icanon). No es realmente una opción, ya que las aplicaciones crudas serían mucho menos utilizables.
  • edite el código del kernel para cambiar el comportamiento de la disciplina de línea tty para que el eco de un carácter de control envíe en \e[7m^X\e[mlugar de solo ^X(aquí \e[7mgeneralmente se habilita el video inverso en la mayoría de los terminales).

Una opción podría ser usar un contenedor como rlwrapese es un truco sucio para agregar un editor de línea elegante a aplicaciones tontas. Ese envoltorio en efecto intenta reemplazar los mensajes simples read()desde el dispositivo terminal a las llamadas al editor de línea readline (que cambian el modo de la disciplina de línea tty).

Yendo aún más lejos, incluso podría probar soluciones como esta que secuestra todas las entradas del terminal para pasar por el editor de línea de zsh (que resalta ^Xs en video inverso) confiando en la :execfunción de la pantalla GNU .

Ahora, para las aplicaciones que implementan su propio editor de línea, depende de ellos decidir cómo se hace el eco . bashusa readline para aquello que no tiene soporte para personalizar cómo se repiten los caracteres de control.

Para zshver:

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

zshresalta caracteres no imprimibles de forma predeterminada. Puede personalizar el resaltado con, por ejemplo:

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

Para resaltar blanco sobre rojo para esos caracteres especiales.

Sin embargo, la representación de texto de esos caracteres no es personalizable.

En una localización UTF-8, 0x18 será mostrado como ^X, \u378, \U7fffffff(dos puntos de código Unicode sin asignar) como <0378>, <7FFFFFFF>, \u200b(un carácter Unicode imprimible no-realidad) como <200B>.

\x80en un entorno local iso8859-1 se representaría como ^�... etc.

Stéphane Chazelas
fuente
3

Usualmente tengo este código en mi archivo .bashrc:

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

y luego llamo a esta función en mi PS1

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

Con esto, si presiona ^ C, lo verá en su mensaje

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

Se le solicitarán todos los códigos de estado de salida que no sean "0".

tocado
fuente
PS se expande para salir del estado en la PS1, por ejemplo, PS1 = '$? \ u @ \ h \ w'
teknopaul