martes, 26 de septiembre de 2017

Personalizando el Prompt

En este capítulo veremos un detalle que aparentemente es trivial - nuestro prompt de shell. Al examinarlo revelaremos algunos de los trabajos internos del shell y el emulador de terminal.

Como muchas cosas en Linux, el prompt de shell es muy configurable, y aunque lo hemos dado por hecho, el prompt es un dispositivo muy útil una vez que hemos aprendido a controlarlo.

Anatomía de un prompt

Nuestro prompt por defecto es algo así:

 [me@linuxbox ~]$

Fíjate que contiene nuestro nombre de usuario, el nombre de nuestro host y nuestro directorio de trabajo actual, pero ¿cómo ha tomado esta forma? Es muy simple. El prompt es definido por la variable de entorno llamada PS1 (abreviatura de “prompt string one” - “cadena de prompt uno”) Podemos ver el contenido de PS1 con el comando echo:

 [me@linuxbox ~]$ echo $PS1
 [\u@\h \W]\$

Nota: No te preocupes si tus resultados no son exactamente los mismos que en el ejemplo. Cada distribución Linux define la cadena del prompt algo diferente, algunas algo exóticamente.

De los resultados, podemos ver que PS1 contiene algunos caracteres que vemos en nuestro prompt como los corchetes, el signo arroba, el signo del dólar, pero el resto son misteriosos. Nuestra astucia los reconocerá como caracteres especiales ocultos tras la barra invertida como los que vimos en el capítulo 7. Aquí hay una lista parcial de los caracteres que el shell trata especialmente en la cadena del prompt:

Códigos de escape usado en los prompts de shell

\a  Tono ASCII. Hace que el ordenador haga un beep cuando lo encuentre.
\d  Fecha actual en formato dia, mes. Por ejemplo, “Lun May 26.”
\h  Nombre de host de la máquina local sin el nombre de dominio delante.
\H  Nombre de host completo.
\j  Número de trabajos corriendo en la sesión de shell actual.
\l  Nombre del dispositivo terminal actual.
\n  Carácter de nueva línea.
\r  Retorno de carro.
\s  Nombre del programa shell.
\t  Hora actual en formato 24 horas. 

Horas:minutos:segundos.
\T  Hora actual en formato 12 horas.
\@  Hora actual en formato 12 horas AM/PM.
\A  Hora actual en formato 24 horas. Horas:minutos.
\u  Nombre de usuario del usuario actual.
\v  Número de versión del shell.
\V  Números de versión y de lanzamiento del shell.
\w  Nombre del directorio de trabajo actual.
\W  Última parte del nombre del directorio de trabajo actual.
\!  Número de historial del comando actual.
\#  Número de comandos introducidos durante esta sesión de shell.
\$  Muestra un carácter “$” a menos que tengas privilegios de superusuario. En ese caso, muestra un “#” en su lugar.
\[  Señal de comienzo de una serie de uno o más caracteres no imprimibles. Se usa para agrupar caracteres de control no imprimibles que manipulará el emulador de terminal de alguna forma, como mover el cursor o cambiar colores de texto.
\]   Señales de fin de una secuencia de caracteres no imprimibles.

Probando algunos diseños de prompt alternativos


Con esta lista de caracteres especiales, podemos cambiar el prompt para ver el efecto. Primero, haremos una copia de seguridad de la cadena existente para poder recuperarla más tarde. Para hacer esto, copiaremos la cadena existente en otra variable de shell que agregaremos nosotros mismos:

 [me@linuxbox ~]$ ps1_old="$PS1"

Creamos una nueva variable llamada ps1_old y le asignamos el valor PS1. Podemos verificar que la cadena ha sido copiada usando el comando echo:

 [me@linuxbox ~]$ echo $ps1_old
 [\u@\h \W]\$

Podemos recuperar el prompt original en cualquier momento de nuestra sesión de terminal simplemente revirtiendo el proceso:

 [me@linuxbox ~]$ PS1="$ps1_old"

Ahora que estamos listos para seguir, veamos que ocurre si tenemos una cadena de prompt vacía:

 [me@linuxbox ~]$ PS1=

Si no asignamos nada a la cadena de prompt, no tendremos nada. ¡Ninguna cadena de prompt! El prompt está ahí, pero no muestra nada, tal como le pedimos. Como así es un poco desconcertante, la reemplazaremos por un prompt minimalista:

 PS1="\$ "

Así está mejor. Al menos ahora podemos ver que estamos haciendo. Fíjate el espacio en blanco delante de las comillas. Esto nos proporciona un espacio entre el signo del dólar y el cursor cuando se muestre el prompt.

Añadamos un tono a nuestro prompt:

 $ PS1="\[\a\]\$ "

Ahora deberíamos escuchar un beep cada vez que se muestre el prompt. Esto podría volverse molesto, pero podría ser util ni necesitamos que nos notifique cuando un comando con un trabajo especialmente largo haya sido ejecutado. Fijate que hemos incluido las secuencias \[ y \]. Como el tono ASCII (\a) no se “imprime”, o sea, no mueve el cursor, necesitamos decirle a bash para que pueda determinar correctamente la longitud del prompt.

A continuación, probemos a hacer un prompt informativo con información sobre el nombre del host y lo hora:

 $ PS1="\A \h \$ "
 17:33 linuxbox $

Añadir la hora a nuestro prompt será útil si necesitamos seguir la pista a la hora en que se han realizado ciertas tareas. Finalmente, haremos un nuevo prompt similar al original:

 17:37 linuxbox $ PS1="<\u@\h \W>\$ "
 <me@linuxbox ~>$

Prueba otras secuencias de la lista anterior a ver si puedes conseguir un nuevo y brillante prompt.

Añadiendo color

La mayoría de los programas emuladores de terminal responden a ciertas secuencias de caracteres no imprimibles para controlar cosas como atributos de caracteres (color, negritas y pavorosos textos parpadeantes) y posiciones del cursor. Veremos la posición del cursor en un rato, pero primero veamos el color.

El color de los caracteres se controla enviando al emulador de terminal un código de escape ANSI incluido en la cadena de caracteres a mostrar. El código de control no se “imprime” en la pantalla, en su lugar es interpretado por la terminal como una instrucción. Como vimos en la tabla anterior, las secuencias \[ y \] se usan para encapsular caracteres no imprimibles. Un código de escape ANSI comienza con un octal 033 (el código generado por la tecla escape), seguido de un atributo de carácter especial, seguido por una instrucción. Por ejemplo, el código para establecer el color del texto a normal (atributo = 0), texto negro es:

 \033[0;30m


Aquí tenemos una tabla con los colores disponibles. Fíjate que los colores se dividen en dos grupos, diferenciados por la aplicación del carácter de atributo negrita (1) que crea la apariencia de colores “luminosos”:

Secuencias de escape usadas para establecer colores de texto

Secuencia   Color del texto  Secuencia   Color del Texto
\033[0;30m  Negro            \033[1;30m  Gris oscuro
\033[0;31m  Rojo             \033[1;31m  Rojo claro
\033[0;32m  Verde            \033[1;32m  Verde claro
\033[0;33m  Marrón           \033[1;33m  Amarillo
\033[0;34m  Azul             \033[1;34m  Azul claro
\033[0;35m  Morado           \033[1;35m  Morado claro
\033[0;36m  Cían             \033[0;36m  Cían claro
\033[0;37m  Gris claro       \033[0;37m  Blanco

Probemos un prompt rojo. Insertaremos el código de escape al principio:

 <me@linuxbox ~>$ PS1="\[\033[0;31m\]<\u@\h \W>\$ " 
 <me@linuxbox ~>$

Funciona, pero fíjate que todo el texto que escribamos tras el prompt también es rojo. Para arreglarlo, añadiremos otro código de escape al final del prompt que le diga al emulador de terminal que vuelve al color previo:

 <me@linuxbox ~>$ PS1="\[\033[0;31m\]<\u@\h \W>\$\[\033[0m\]"
 <me@linuxbox ~>$

¡Así está mejor!

También es posible establecer el color de fondo usando los códigos listados anteriormente. Los colores de fondo no soportan el atributo negrita.

Secuencias de escape usadas para establecer el color de fondo

Secuencia   Color de fondo  Secuencia   Color de fondo
\033[0;40m  Negro           \033[0;44m  Azul
\033[0;41m  Rojo            \033[0;45m  Morado
\033[0;42m  Verde           \033[0;46m  Cían
\033[0;43m  Marrón          \033[0;47m  Gris claro

Podemos crear un prompt con un fondo rojo aplicando un simple cambio al primer código de escape:

 <me@linuxbox ~>$ PS1="\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\]"
 <me@linuxbox ~>$

Prueba los códigos de color y ¡Mira lo que puedes crear!

Nota: Además de los atributos de carácter normal (0) y negrita (1), el texto también puede tiene atributos para subrayado (4), parpadeante (5) e invertido (7). En beneficio del buen gusto, muchos emuladores de terminal rechazan el honor de tener el atributo parpadeante.


Confusión con el Terminal


Volviendo a los viejos tiempos, cuando los terminales estaban conectados a ordenadores remotos, habías muchas marcas de terminales compitiendo y todas funcionaban de forma diferente. Tenían teclados diferentes y todas tenían diferentes formas de interpretar la información de control. Los sistemas Unix y como-Unix tiene dos subsistemas complejos para tratar la torre de babel del control del terminal (llamados termcap y terminfo). Si miras en lo más profundo de la configuración de tu emulador de terminal encontrarás un parámetro para el tipo de emulador de terminal.

En un esfuerzo para hacer que los terminales hablen un tipo de lenguaje común, El American National Standards Institute (ANSI) desarrolló un catálogo de secuencias de caracteres para controlar los terminales de video. Los usuarios antiguos de DOS recordarán el archivo ANSI.SYS que se utilizaba para permitir la interpretación de dichos códigos.


Moviendo el cursor

Los códigos de escape pueden ser usados para posicionar el cursor. Se usa comúnmente para proporcionar un reloj o algún otro tipo de información en una localización diferente de la pantalla, como en la esquina superior cada vez que se dibuja el prompt. Aquí tenemos una lista de los códigos de escape que posicionan el cursor:

Secuencias de escape de movimiento del cursor

Código de escape  Acción
\033[1,cH         Mueve el cursor a la línea l y columna c
\033[nA           Mueve el cursor arriba n lineas
\033[nB           Mueve el cursor abajo n lineas
\033[nC           Mueve el cursor hacia delante n caracteres
\033[nD           Mueve el cursor hacia atrás n caracteres
\033[2J           Limpia la pantalla y mueve el cursor a la

                  esquina superior izquierda (línea 0, columna 0)
\033[K            Limpia desde la posición del cursor hasta

                  el final de la línea actual
\033[s            Almacena la posición actual del cursor
\033[u            Recupera la posición del cursor almacenada

Usando los códigos anteriores, construiremos un prompt que dibuje una línea roja arriba de la pantalla conteniendo un reloj (dibujado en texto amarillo) cada vez que se muestre el prompt. El código para el prompt es esta cadena de aspecto formidable:

PS1="\\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\]<\u@\h \W>\$ "


Echemos un vistazo a cada parte de la cadena para ver que hace:

Desglose de cadenas de prompt complejas

\[
Comienza una secuencia de caracteres no imprimibles. El propósito de ésto es permitir que bash calcule apropiadamente el tamaño del prompt visible. Sin un calculo preciso, las funciones de edición de la línea de comandos no pueden posicionar el cursor apropiadamente.

\033[s
Almacena la posición del cursor. Se necesita para volver a la localización del prompt después de que la barra y reloj hayan sido dibujados en la parte superior de la pantalla. Ten en cuenta que ciertos emuladores de terminal no soportan este código.

\033[0;0H
Mueve el cursor a la esquina superior izquierda, que es la línea 0, columna 0.

\033[0;41m
Establece el color de fondo a rojo.

\033[K
Borra la posición actual del cursor (la esquina superior izquierda) al final de la línea. Como el color de fondo es ahora rojo, la línea se borra a ese color creando nuestra barra. Nota que borrar hasta el final de la línea no cambia la posición del cursor, que permanece en la esquina superior izquierda.

\033[1;33m
Establece el color a amarillo

\t
Muestra la hora actual. Como es un elemento “imprimible”, lo incluimos todavía dentro de la porción no imprimible del prompt, ya que no queremos que bash incluya el reloj cuando calcule el tamaño real del prompt mostrado.

\033[0m
Apaga el color. Afecta tanto al texto como al fondo.

\033[u
Recupera la posición del cursor salvado anteriormente.

\]
Fin de la secuencia de caracteres no imprimibles.

<\u@\h \W>\$
Cadena de prompt.


Salvando del prompt

Obviamente, no queremos escribir este monstruo todo el tiempo, así que querremos almacenar nuestro prompt en alguna parte. Podemos hacer el prompt permanente añadiéndolo a nuestro archivo .bashrc. Para hacerlo, añade estas dos líneas al archivo:

PS1="\
[033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u\]
<\u@\h \W>\$ "

export PS1

Resumiendo


Lo creas o no, hay mucho más que puede hacerse con los prompts incluyendo funciones de shell y scripts que no hemos visto aquí, pero es un buen comienzo. No todo el mundo va a preocuparse de cambiar el prompt, ya que el prompt por defecto normalmente es satisfactorio. Pero para aquellos de nosotros a los que nos gusta juguetear, el shell nos brinda la oportunidad de pasar muchas horas de diversión trivial.

Para saber más

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.