Modificando el prompt de bash Por Xento Figal (c) 2004 bajo licencia FDL • Objeto del documento • ¿Que es el prompt? • ¿Donde se encuentran los archivos del prompt? • ¿Por que modificar el prompt? • Modificando el prompt • Variables de entorno • Secuencias de escape Comunes Colores Movimientos del cursor Trabajando con funciones • Funciones como variables. Funciones como programas. Ejemplos utiles (o inutiles) • Prompt extenso con funciones especiales. Prompt con mucha informacion y lento. • Documentacion a tener en cuenta • Sobre el documento Objeto del documento El presente documento pretende servir de ayuda a quienes nunca han modificado o usado estas caracteristicas de bash. Con ejemplos utiles y explicaciones sencillas, no se pretende por tanto abarcar los aspectos mas complejos relacionados con bash, aqui tan solo se dara una idea global de como modificar el aspecto del interprete de bash, para documentacion mas extendida sobre bash consulte la seccion documentacion. Si usted usa un shell distinto a bash, tal vez este documento no le sea de mucha utilidad. indice ¿Que es el prompt? El prompt es el interprete de bash, el interprete posee una apariencia modificable , generalmente es esta: [usuario/dominio directorio]$ Esto realmente no es mas que una salida de comandos a una variable de nuestro shell, la variable es $PS1, existen mas variables pero nos vamos a centrar en la que nos interesa (PS1) que es donde se define el aspecto de nuestro prompt. indice ¿Donde se encuentra el prompt? El prompt de linux se puede definir en la variable de entorno $PS1 que a su vez se puede definir en el archivo .bashrc en el $HOME, o bien de manera general para toda la maquina en /etc/bashrc, si optamos por modificar el prompt de el usuario o usuarios, deberemos modificar la variable $PS1 de $HOME~.bashrc de lo contrario modificaremos el de toda la maquina, sea quien sea el usuario (/etc/bashrc). Aunque esto tratandose de Linux tambien se puede modificar como veremos mas adelante, pero de momento dejemos este concepto como esta. indice ¿Por que modificar el prompt? Principalmente nos basamos en la utilidad, en la comodidad y en el control visual de la propia maquina. En el prompt podemos visualizar lo que se nos ocurra, al contrario de otros sistemas, con bash podemos almacenar una cantidad importante de datos en memoria, esto en MsDos era casi inpensable, logicamente hay que tener en cuenta que; a cuanta mas memoria = uso mas lento de la interfaz de comandos, esto se hace particularmente importante cuando introducimos muchos comandos, variables, o su salida si se prefiere asi, en el prompt, a este concepto se hace referencia en otra parte del documento. En los sistemas multiusuario la modificacion del prompt puede ser muy util, puesto que nos permite mostrar el texto que nosotros deseemos, esto nos abre un abanico muy extenso de opciones, P.ejm: mostrar las cuotas de los usuarios, los servicios a los que tienen acceso, etc... indice Modificando el prompt Nos vamos a basar en un archivo bashrc muy sencillo, e iremos comentando las lineas y/o instrucciones de este, por supuesto el ejemplo es extensible hasta donde queramos llegar. # /etc/bashrc if [ "`id ­gn`" = "`id ­un`" ­a `id ­u` ­gt 99 ]; then umask 002 else umask 022 fi <­­Lo primero que hacemos es definir umask, umask nos permite tener una configuracion por defecto en materia de ficheros, directorios y permisos. Buscamos el id del usuario, filtrando las cuentas que no son menores de 100, esto es por que esas cuentas que estan por debajo de 100 corresponden a "usuarios propios del sistema" (mail, news, apache, etc....) ­­> [ "$PS1" = "\\s­\\v\\\$ " ] && PS1="\[\h@\u \w]" <­­ en esta linea definimos la variable PS1, esta es la linea del prompt en si, en este caso veriamos el nombre del host, el simbolo @ el usuario y el directorio actual de trabajo ­­> Probablemente el aspecto de un archivo bashrc sera mas amplio, incorporando mas funciones. Pero sin lugar a dudas lo que es evidente, es que es un script para el bash y como tal se puede modificar "al gusto", vamos a ver como podemos hacer un prompt mas personal. indice Variables de entorno Nos tenemos que referir a ellas casi por obligacion, una variable de entorno es como cualquier otra, un nombre asociado a una cadena de caracteres, esta cadena de caracteres contienen ciertos parametros del propio sistema, como por ejemplo el editor de texto por defecto, los colores definidos al ejecutar un comando:P.ejm ls (tipo de archivo = color, etc...). Nosotros nos vamos a centrar en las que nos interesan; $SHELL , esta es la variable donde se almacenan los datos del shell (este documento se centra en bash, tenga en cuenta que para otra shell no podra aplicar esto) en nuestro caso deberiamos tener definida bash $PS1 , En esta se almacena la informacion que mostrar el prompt, el ambito de este documento se centra en esta variable. • • Para trabajar con las variables de entorno se usan los comandos set y env. Si quiere ver las variables asignadas use env, para modificarlas use set, tambien puede ver el valor de una variable escribiendo echo Nombre_de_la_variable. P.ejem: $set ­u SHELL=BASH Borrariamos la variable SHELL (vea man set o set ­­help para poder conocer todas opciones). indice Secuencias de escape La secuencias de escape nos seran de muchisima utilidad, puesto que muestran informacion en el prompt si tener que escribir codigo nuevo. Secuencias comunes \a \d \e \h \H \n \r \s \t \T \u \v \V \w \W \@ \# caracter de campana ASCII(07) Fecha en formato alfanumerico, dia mes hora caracter de escape ASCII(033) Nombre del dominio sin dominio Nombre completo del host caracter de nueva línea Retorno de carro Caracter que sigue a la última barra Hora actual en formato 24­horas HH:MM:S Hora actual en formato 12­horas HH:MM:SS Nombre del usuario del shell La versión del Bash La versión del paquete del Bash El directorio actual de trabajo El nombre completo del directorio actual de trabajo, si esta en /usr/local La hora actual en formato 12­horas am/pm El numero de comando del comando actual El numero del comando actual en el historico, esto puede ser util para ciertos \! scripts \$ Si el UID efectivo es 0, una #, sino, un $, es decir si es root o pertence: 0, si no: $ \[ Inicio de una secuencia de caracteres no imprimibles \] Fin de una secuencia de caracteres no imprimibles \nnn Un caracter correspondiente al numero octal nn \\ Una contra barra En bash tambien podemos colorear los resultados, esto tambien se hace desde $PS1, asi como para poder ver los archivos en colores segun la extension, esta definido en una variable de entorno (P.ejm: LSCOLORS) para el prompt se define en la variable, pero no como una variable mas de entorno, si no dentro de la misma ($PS1). Para incluir colores debemos encerrarlos entre \[\033[colorm\] donde color es cualquiera de los codigos que se encuentran en la tabla de abajo, la "m" es necesaria. Para terminar con el codigo de color, \[\033[0m\], en caso contrario todo el texto que aparecera en el buffer sera del color que previamente hemos definido. indice Tablas de colores Negro Azul Verde Cyan Rojo Purpura Marron Gris claro Gris oscuro Azul claro Verde claro Cyan claro Rojo claro Purpura claro Amarillo blanco 0;30 0;34 0;32 0;36 0;31 0;35 0;33 0;37 1;30 1;34 1;32 1;36 1;31 1;35 1;33 1;37 Por lo tanto, si quisieramos mostrar la fecha en verde seria: PS1="\[\033[0;32m\][\$(date +%H%M)]$\[\033[0m\]" indice Movimiento del cursor Por otro lado, en algunas ocasiones nos puede ser muy util cambiar otros aspectos, como el cursor, esto tiene mas usabilidad en scripts que en el propio prompt. \033[;H \033[A \033[B \033[C \033[D \033[s \033[u Coloca el cursor en la linea L, columna C. Mueve el cursor arriba N lineas Mueve el cursor abajo N lineas Mueve el cursor hacia delante N columnas Mueve el cursor hacia atras N columnas Guarda la posicion del cursor Restaura la posicion del cursor indice Trabajando con funciones Una funcion es un trozo de codigo dentro de un programa que se ejecuta como si fuese codigo independiente, puede ver mas sobre funciones aqui. En nuestro caso usaremos funciones para definir aspectos de la variable $PS1, es decir usaremos funciones para declarar variables que posteriormente seran mostradas en PS1, la funcion mas basica es esta: function funcion_ejemplo { echo "mi primera funcion" } Si usaramos esta funcion en la variable $PS1 ($PS1="$funcion_ejemplo") nos devolveria un prompt como este: mi primera funcion$ Teniendo esto en cuenta, podemos crear funciones que nos devolveran aspectos interesantes, por ejemplo podemos hacer una funcion que nos muestre el consumo de memoria, el estado de samba, nuestra i.p publica, el espacio del disco, los archivos con una UID 0 dentro del mismo directorio de trabajo u otro, etc, etc, hay que tener en cuenta que para trabajar con funciones es necesario un conocimiento previo de como hacer scripts para bash, tambien hay que tener en cuenta que si mostramos algo demasiado largo, o que necesite mucho tiempo, repercutira en la rapidez y el aspecto del prompt. En este ejemplo se ha usado la funcion para definir una variable, de igual manera se pueden declarar variables como si de cualquier script se tratase. Funciones como programas. Otra manera de aprovechar el bash, es crear pequeñas aplicaciones, son pequeños scripts dentro de bashrc, se trata de pequeños trozos de codigo que trabajan de manera "autonoma" son otro tipo de trabajo con funciones, pero que en este caso al tratarse directamente sobre bashrc las podremos usar cuando estemos en nuestra shell, interesante ¿no?. Sabiendo que podemos definir el bashrc de cada usuario, esto se presenta de una utilidad importante, pudiendo crear pequeños programas sin necesidad de que los usen otros usuarios, o facilitar tareas comunes. P.ejm: calcula() { echo "$*" | bc ­l } Este ejemplo, si lo incluyeramos dentro de nuestro bashrc nos permitira usar calcula para ejecutar operaciones sobre bc. [xento@k2 \home]$ calcula 2.3 ^3 12.167 [xento@k2 \home]$ indice Ejemplos utiles o inutiles. A continuacion nos dejamos de tanta teoria y hacemos un poco de practica, un par de prompts potentes, eso si hay una diferencia entre los dos, el primero resulta mas rapido, pero muestra menos informacion en la linea del prompt, el segundo en cambio muestra mas informacion en la linea del prompt, pero a cambio es mas lento. Prompt extenso # /etc/bashrc if [ "`id ­gn`" = "`id ­un`" ­a `id ­u` ­gt 99 ]; then umask 002 else umask 022 fi if [ "`id ­u`" ­gt 99 ]; then . /etc/bashrc­luser fi if [ "$PS1" ]; then case $TERM in xterm*) PROMPT_COMMAND='echo ­ne "\033]0;${HOSTNAME}: ${PWD}\007"' ;; *) ;; esac infored () { out=$(ifconfig eth0 | grep ­i RX | awk '/bytes/ {print $3$4 }') in=$(ifconfig eth0 | grep ­i RX | awk '/bytes/ {print $7$8 }') ip=$(ifconfig eth0 | awk '/addr/ {print $2 }'| cut ­d: ­f2 ) netuna=$(netstat ­tuna | grep ­v Active ) conactv=$(lsof | grep SYN_SENT) conescucha=$(lsof ­i | grep LISTEN) echo ­e "\033[0;35m[1]\033[0m" "Ver informacion en pantalla" echo ­e "\033[0;35m[2]\033[0m" "Ver informacion por puerto" echo ­e "\033[0;35m[3]\033[0m" "Ver informacion por proceso" read opcion case $opcion in 1) echo ­e "Salida:\033[0;34m$out\033[0m " " Entrada:\033[0;31m$in\033[0m " " IP:$ip " echo "Conexiones de internet (servicios y establecidas)" echo "$netuna" echo "Procesos con conexion" echo "$conactv" echo "Procesos esperando conexion" echo "$conescucha" ;; 2) read puerto var1=$(lsof ­i :$puerto) echo "$var1" ;; 3) read proceso var2=$(lsof | grep $proceso) echo "$var2" ;; esac } function memf { exec free ­m | grep Mem | awk '{print $4}' } function memt { exec free ­m | grep Mem | awk '{print $3}' } function cuotas { quotastats | grep drops | awk '{print $5}' } [ "$PS1" = "\\s­\\v\\\$ " ] && PS1="\[[$(cuotas)] [\033[1;31m\]$(memt)\[\033[0m\]­\[\033[1;34m\] $(memf)\[\033[0m\] \W]" if [ "x$SHLVL" != "x1" ]; then # We're not a login shell for i in /etc/profile.d/*.sh; do if [ ­r "$i" ]; then . $i fi done fi fi Como podreis ver dentro de bashrc he creado un par de scripts para poder usarlos desde mi shell, principalmente defino por un sitio que si no se es root o se tiene 0 como uid se ejecuta el archivo /etc/bashrc­lusers, de lo contrario se continua, ademas en la funcion infored, introducimos un pequeño programa para mostrar informacion util, despues tambien usamos algunas funciones para declarar variables y mostrarlas en el mismo prompt. Prompt que muestra mucha informacion y es muy lento. # /etc/bashrc if [ "`id ­gn`" = "`id ­un`" ­a `id ­u` ­gt 99 ]; then umask 002 else umask 022 fi if [ "$PS1" ]; then if [ ­x /usr/bin/tput ]; then if [ "x`tput kbs`" != "x" ]; then # We can't do this with "dumb" terminal stty erase `tput kbs` elif [ ­x /usr/bin/wc ]; then if [ "`tput kbs|wc ­c `" ­gt 0 ]; then # We can't do this with "dumb" terminal stty erase `tput kbs` fi fi fi case $TERM in xterm*) if [ ­e /etc/sysconfig/bash­prompt­xterm ]; then PROMPT_COMMAND=/etc/sysconfig/bash­prompt­xterm else PROMPT_COMMAND='echo ­ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~}\007"' fi ;; screen) PROMPT_COMMAND='echo ­ne "\033_${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~}\033\\"' ;; *) [ ­e /etc/sysconfig/bash­prompt­default ] && PROMPT_COMMAND=/etc/sysconfig/bash­prompt­default ;; esac function uptaim { perl ­le 'my $uptimeinfo = `uptime`;if ($uptimeinfo =~ /^\s+(\d+:\d+\w+|\d+:\d+:\d+)\s+up\s+(\d+)\s+day.?\W\s+(\d+): (\d+)\W\s+(\d+)\s+\ w+\W\s+\w+\s+\w+\W\s+(\d+).(\d+)/igx) {print("$2d $3h $4m");}elsif ($uptimeinfo =~ /^\s+ (\d+:\d+\w+|\d+:\d+:\d+)\s+up+\s+(\d+):(\d+)\W\s+(\d+)\s+\w+\W\s+\w+\s+\w+\W\s+( \d+).(\d+)/igx) {print("$2h $3m");}elsif ($uptimeinfo =~ /^\s+(\d+:\d+\w+|\d+:\d+:\d+)\s+up\s+(\d+)\s+day.?\W\s+(\d+)\s+min\W\s+(\d+)\s+\ w+\W\s+\w+\s+\w+\W\s+(\d+).(\d+)/igx) {print("$2d $3m");}elsif ($uptimeinfo =~ /^\s+(\d+:\d+\w+|\d+:\d+:\d+) \s+up+\s+(\d+)\s+min\W\s+(\d+)\s+\w+\W\s+\w+\s+\w+\W \s+(\d+).(\d+)/igx) {print("$2m");}print undef;' } function discolibre { perl ­le 'my($disktotal,$diskusedi);for(`df`){chomp;next if !/^\/dev\/\S+/;next if /(cd|cdrom|fd|floppy)/;/^(\S+)\s+(\S+)\s+ (\S+)/;$disktotal += $2;$diskused += $3;}$disktotal = sprintf("%.2f",$disktotal / 1024 / 1024);$diskused = sprintf("%. 2f",$diskused / 1024 / 1024);print("$diskused­$disktotal");' } function memf { exec free ­m | grep Mem | awk '{print $4}' } function memt { exec free ­m | grep Mem | awk '{print $3}' } function cpu { exec top ­n1 | grep "CPU" | awk '{print $3+$5"%"}' } function proc { exec top ­n1 | grep processes | awk '{print $1}' } function date2 { exec date | awk '{print $1"­"$3"­"$2" "$6 }' } function users { exec top ­n1 | grep users | awk '{print $5" Users"}' } [ "$PS1" = "\\s­\\v\\\$ " ] && PS1="\[[HD: $(discolibre) GB][Ram: \[\033[1;31m\]$(memt)\[\033[0m\]­\[\033[1;34m\] $(memf)\[\033[0m\] MB][$(cpu) Cpu][$(proc) Proc][$(users)] [$(date2)][$(date +%H:%M)]\[\033[0m\][Total: $(uptaim)] [\w] [\u\[\033[1;32m\]@\[\033[0m\]\h\[\033[0m\]:]\ \[\033[1;32m\]$\[\033[0m\]" if [ "x$SHLVL" != "x1" ]; then # We're not a login shell for i in /etc/profile.d/*.sh; do if [ ­r "$i" ]; then . $i fi done fi fi Este prompt mostraria mucha informacion, pero es realmente pesado y lento, aqui no hemos trabajado mucho en funciones que nos permitan ejecutar codigo a voluntad, si no que directamente les hemos asignado variables y las mostramos en el prompt. Se espera que se ilustre bien como modificar un prompt y hacerlo practico a nuestras necesidades, estos ejemplos no son quizas la mejor manera de hacer las cosas, pero resultan utiles para explicar en que consiste una modificacion del prompt. indice Documentacion a tener en cuenta ­[http://xinfo.sourceforge.net/documentos/bash­scripting/] Una introduccion a la programacion en bash. ­[http://www.tldp.org/LDP/abs/html/] El imprescindible how­to sobre bash scripting. indice Sobre el documento El presente documento se encuentra bajo terminos de la licencia FDL, se distribuye sin ninguna garantia, esta autorizada la libre distribucion, modificacion y copia, rogando que se notifique al autor cambios y aportaciones, e incluyendo al autor original cuando se haga copia total o parcial o cita del mismo, para ver mas detalles consulte la licencia GNU/FDL. 2004 (c) Xento Figal Version 1.0 EOF indice