variable prompts personalizar maker color change beautiful bash ps1

bash - prompts - Echo expandido PS1



personalizar prompt bash (7)

Dos respuestas: "Pure bash" y "bash + sed"

Como hacer esto usando sed es más simple, la primera respuesta usará sed .

Vea a continuación la solución pura de bash .

expansión rápida de bash , bash + sed

Ahí está mi truco:

ExpPS1="$(bash --rcfile <(echo "PS1=''$PS1''") -i <<<'''' 2>&1 | sed '':;$!{N;b};s/^/(.*/n/)*/(.*/)/n/2exit$//2/p;d'')"

Explicación:

Ejecutando bash --rcfile <(echo "PS1=''$PS1''") -i <<<'''' 2>&1

Puede devolver algo como:

To run a command as administrator (user "root"), use "sudo <command>". See "man sudo_root" for details. ubuntu@ubuntu:~$ ubuntu@ubuntu:~$ exit

El comando sed entonces

  • tomar todas las líneas en un búfer ( :;$!{N;b}; ), que
  • Reemplace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit por <prompt> . ( s/^/(.*/n/)*/(.*/)/n/2exit$//2/ ).
    • donde <everything, terminated by end-of-line> convierte en /1
    • y <prompt> convierte en /2 .
Caso de prueba:

while ExpPS1="$(bash --rcfile <(echo "PS1=''$PS1''") -i <<<'''' 2>&1 | sed '':;$!{N;b};s/^/(.*/n/)*/(.*/)/n/2exit$//2/p;d'')" read -rp "$ExpPS1" && [ "$REPLY" != exit ] ;do eval "$REPLY" done

A partir de ahí, estás en una especie de shell pseudoactivo (sin instalaciones de línea de lectura, pero eso no importa) ...

ubuntu@ubuntu:~$ cd /tmp ubuntu@ubuntu:/tmp$ PS1="${debian_chroot:+($debian_chroot)}/[/e[1;32m/]/u/[/e[0m/]@/[/e[1;32m/]/h/[/e[0m/]:/[/e[1;34m/]/w/[/e[0m/]$ " ubuntu@ubuntu:/tmp$

(La última línea imprime tanto ubuntu en verde, @ ubuntu y $ en negro como la ruta ( /tmp ) en azul)

ubuntu@ubuntu:/tmp$ exit ubuntu@ubuntu:/tmp$ od -A n -t c <<< $ExpPS1 033 [ 1 ; 3 2 m u b u n t u 033 [ 0 m @ 033 [ 1 ; 3 2 m u b u n t u 033 [ 0 m : 033 [ 1 ; 3 4 m ~ 033 [ 0 m $ /n

Puro bash

ExpPS1="$(bash --rcfile <(echo "PS1=''$PS1''") -i <<<'''' 2>&1)" ExpPS1_W="${ExpPS1%exit}" ExpPS1="${ExpPS1_W##*$''/n''}" ExpPS1_L=${ExpPS1_W%$''/n''$ExpPS1} while [ "${ExpPS1_W%$''/n''$ExpPS1}" = "$ExpPS1_W" ] || [ "${ExpPS1_L%$''/n''$ExpPS1}" = "$ExpPS1_L" ] ;do ExpPS1_P="${ExpPS1_L##*$''/n''}" ExpPS1_L=${ExpPS1_L%$''/n''$ExpPS1_P} ExpPS1="$ExpPS1_P"$''/n''"$ExpPS1" done

El bucle while es necesario para garantizar el manejo correcto de las solicitudes de varias líneas:

Reemplace la 1ª línea por:

ExpPS1="$(bash --rcfile <(echo "PS1=''${debian_chroot:+($debian_chroot)}/[/e[1;32m/]/u/[/e[0m/]@/[/e[1;32m/]/h/[/e[0m/]:/[/e[1;34m/]/w/[/e[0m/]$ ''") -i <<<'''' 2>&1)"

o

ExpPS1="$(bash --rcfile <(echo "PS1=''Test string/n$(date)/n$PS1''") -i <<<'''' 2>&1)";

La última multilínea imprimirá:

echo "$ExpPS1" Test string Tue May 10 11:04:54 UTC 2016 ubuntu@ubuntu:~$ od -A n -t c <<<${ExpPS1} T e s t s t r i n g /r T u e M a y 1 0 1 1 : 0 4 : 5 4 U T C 2 0 1 6 /r 033 ] 0 ; u b u n t u @ u b u n t u : ~ /a u b u n t u @ u b u n t u : ~ $ /n

Tengo un script de shell que ejecuta el mismo comando en varios directorios ( fgit ). Para cada directorio, me gustaría mostrar el indicador actual + el comando que se ejecutará allí. ¿Cómo obtengo la cadena que corresponde a la PS1 decodificada (expandida)? Por ejemplo, mi PS1 por defecto es

${debian_chroot:+($debian_chroot)}/[/e[1;32m/]/u/[/e[0m/]@/[/e[1;32m/]/h/[/e[0m/]:/[/e[1;34m/]/w/[/e[0m/]$(__git_ps1 '' (%s)'')$

y me gustaría hacer un eco del mensaje de username@hostname:/path$ resultante username@hostname:/path$ , preferiblemente (pero no necesariamente) con los colores agradables. Una mirada rápida al manual de Bash no reveló ninguna respuesta definitiva, y echo -e $PS1 solo evalúa los colores.


¿Por qué no $PS1 tú mismo las sustituciones de escape de $PS1 ? Una serie de sustituciones como estas:

p="${PS1////u/$USER}"; p="${p////h/$HOSTNAME}"

Por cierto, zsh tiene la capacidad de interpretar los escapes del sistema.

print -P ''%n@%m %d''

o

p=${(%%)PS1}


Desde Bash 4.4 puedes usar la expansión @P :

Primero puse su cadena de solicitud en una variable myprompt usando read -r y una cita aquí-doc:

read -r myprompt <<''EOF'' ${debian_chroot:+($debian_chroot)}/[/e[1;32m/]/u/[/e[0m/]@/[/e[1;32m/]/h/[/e[0m/]:/[/e[1;34m/]/w/[/e[0m/]$(__git_ps1 '' (%s)'')$ EOF

Para imprimir el aviso (como se interpretaría si fuera PS1 ), use la expansión ${myprompt@P} :

$ printf ''%s/n'' "${myprompt@P}" gniourf@rainbow:~$ $

(De hecho, hay algunos caracteres /001 y /002 , que vienen de /[ y /] que no puedes ver aquí, pero puedes verlos si intentas editar esta publicación; también los verás en tu terminal si escribe los comandos).

Para deshacerse de estos, el truco enviado por Dennis Williamson en la lista de correo de bash es usar read -e -p para que la biblioteca readline interprete estos caracteres:

read -e -p "${myprompt@P}"

Esto indicará al usuario, con el myprompt correctamente interpretado.

A esta publicación, Greg Wooledge respondió que también podrías quitar el /001 y /002 de la cadena. Esto se puede lograr así:

myprompt=${myprompt@P} printf ''%s/n'' "${myprompt//[$''/001''$''/002'']}"

A esta publicación, Chet Ramey respondió que también podría desactivar la edición de línea en conjunto con set +o emacs +o vi . Así que esto también lo hará:

( set +o emacs +o vi; printf ''%s/n'' "${myprompt@P}" )


Es posible que tenga que escribir un pequeño programa en C que use el mismo código que hace bash (¿es una llamada de la biblioteca?) Para mostrar ese aviso, y solo llame al programa C. Por supuesto, eso no es muy portátil, ya que tendrá que compilarlo en cada plataforma, pero es una solución posible.


Me gusta la idea de arreglar a Bash para mejorarlo, y aprecio la respuesta detallada de paxdiablo sobre cómo parchear a Bash. Voy a tener una oportunidad en algún momento.

Sin embargo, sin parchear el código fuente de Bash, tengo un hack de una sola línea que es portátil y no duplica la funcionalidad, porque la solución utiliza solo Bash y sus componentes integrados.

x="$(PS1=/"$PS1/" echo -n | bash --norc -i 2>&1)"; echo "''${x%exit}''"

Tenga en cuenta que hay algo extraño en tty ''s y stdio ya que esto también funciona:

x="$(PS1=/"$PS1/" echo -n | bash --norc -i 2>&1 > /dev/null)"; echo "''${x%exit}''"

Entonces, aunque no entiendo lo que está pasando con el stdio aquí, mi truco está funcionando para mí en Bash 4.2, NixOS GNU / Linux. Parchar el código fuente de Bash es definitivamente una solución más elegante, y debería ser bastante fácil y seguro ahora que estoy usando Nix.


Una gran ventaja del software de código abierto es que la fuente es, bueno, abierta :-)

Bash no proporciona esta funcionalidad, pero hay varios trucos que puede usar para proporcionar un subconjunto (como sustituir /u con $USER y así sucesivamente). Sin embargo, esto requiere mucha duplicación de funcionalidad y asegurar que el código se mantenga sincronizado con lo que haga bash en el futuro.

Si desea obtener todo el poder de las variables de solicitud (y no le importa ensuciarse las manos con un poco de codificación (y, si le importa, ¿por qué está aquí?)), Es bastante fácil de agregar a la shell en sí.

Si descarga el código para bash (estoy viendo la versión 4.2), hay un archivo y.tab.c que contiene la función decode_prompt_string() :

char *decode_prompt_string (string) char *string; { ... }

Esta es la función que evalúa las variables PSx para la PSx . Para permitir que esta funcionalidad se proporcione a los usuarios del shell en sí (en lugar de simplemente usarlo), puede seguir estos pasos para agregar un comando interno evalps1 .

Primero, cambie support/mkversion.sh para que no lo confunda con un bash "real", y para que la FSF pueda negar todo conocimiento con fines de garantía :-) Simplemente cambie una línea (agregué el bit -pax ) :

echo "#define DISTVERSION /"${float_dist}-pax/""

Segundo, cambie `builtins / Makefile.in para agregar un nuevo archivo fuente. Esto conlleva una serie de pasos.

(a) Agregue $(srcdir)/evalps1.def al final de DEFSRC .

(b) Agregue evalps1.o al final de OFILES .

(c) Agregue las dependencias requeridas:

evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h / $(topdir)/bashintl.h $(topdir)/shell.h common.h

Tercero, agregue el propio archivo builtins/evalps1.def , este es el código que se ejecuta cuando ejecuta el comando evalps1 :

This file is evalps1.def, from which is created evalps1.c. It implements the builtin "evalps1" in Bash. Copyright (C) 1987-2009 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see <http://www.gnu.org/licenses/>. $PRODUCES evalps1.c $BUILTIN evalps1 $FUNCTION evalps1_builtin $SHORT_DOC evalps1 Outputs the fully interpreted PS1 prompt. Outputs the PS1 prompt, fully evaluated, for whatever nefarious purposes you require. $END #include <config.h> #include "../bashtypes.h" #include <stdio.h> #include "../bashintl.h" #include "../shell.h" #include "common.h" int evalps1_builtin (list) WORD_LIST *list; { char *ps1 = get_string_value ("PS1"); if (ps1 != 0) { ps1 = decode_prompt_string (ps1); if (ps1 != 0) { printf ("%s", ps1); } } return 0; }

La mayor parte de eso es la licencia GPL (ya que la modifiqué desde exit.def ) con una función muy simple al final para obtener y decodificar PS1 .

Por último, basta con construir la cosa en el directorio de nivel superior:

./configure make

El ejecutable de bash que aparece puede ser renombrado a paxsh , aunque dudo que alguna vez sea tan frecuente como su antecesor :-)

Y ejecutándolo, puedes verlo en acción:

pax> mv bash paxsh pax> ./paxsh --version GNU bash, version 4.2-pax.0(1)-release (i686-pc-linux-gnu) Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. pax> ./paxsh pax> echo $BASH_VERSION 4.2-pax.0(1)-release pax> echo "[$PS1]" [pax> ] pax> echo "[$(evalps1)]" [pax> ] pax> PS1="/h: " paxbox01: echo "[$PS1]" [/h: ] paxbox01: echo "[$(evalps1)]" [paxbox01: ]

Cuando coloca una de las variables de PSx en el indicador, el eco de $PS1 simplemente le da la variable, mientras que el comando evalps1 evalúa y genera el resultado.

Ahora, dado que algunos cambios en el código de bash para agregar un comando interno pueden ser considerados excesivos, pero si quieres una evaluación perfecta de PS1 , es sin duda una opción.


Una posibilidad más: sin editar el código fuente de bash, usando la utilidad de script (parte del paquete bsdutils en ubuntu):

$ TEST_PS1="/e[31;1m/u@/h:/n/e[0;1m/$ /e[0m" $ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1 $ script /dev/null <<-EOF | awk ''NR==2'' RS=$RANDOM_STRING PS1="$TEST_PS1"; HISTFILE=/dev/null echo -n $RANDOM_STRING echo -n $RANDOM_STRING exit EOF <prints the prompt properly here>

script comando de script genera un archivo especificado y la salida también se muestra en la salida estándar. Si se omite el nombre de archivo, genera un archivo llamado mecanografiado.

Como no estamos interesados ​​en el archivo de registro en este caso, el nombre de archivo se especifica como /dev/null . En su lugar, el stdout del comando de script se pasa a awk para su posterior procesamiento.

  1. El código completo también se puede encapsular en una función.
  2. Además, la solicitud de salida también se puede asignar a una variable.
  3. Este enfoque también admite el análisis de PROMPT_COMMAND ...