¿Cuál es la diferencia entre PS1 y PROMPT_COMMAND?

108

Mientras echaba un vistazo a este increíble hilo , noté que algunos ejemplos usan

PS1="Blah Blah Blah"

y algo de uso

PROMPT_COMMAND="Blah Blah Blah"

(y algunos usan ambos) al configurar el indicador en un shell bash. ¿Cuál es la diferencia entre los dos? Una búsqueda SO e incluso una búsqueda más amplia en Google no me están dando resultados, por lo que incluso un enlace al lugar correcto para buscar la respuesta sería apreciado.

Jed Daniels
fuente

Respuestas:

59

Desde la página de documentación de GNU Bash: http://www.gnu.org/software/bash/manual/bashref.html

PROMPT_COMMAND
    If set, the value is interpreted as a command to execute before
    the printing of each primary prompt ($PS1).

Nunca lo usé, pero podría haberlo usado cuando solo tenía sh.

Scott Thomson
fuente
67

PROMPT_COMMAND puede contener declaraciones bash ordinarias, mientras que la variable PS1 también puede contener caracteres especiales, como '\ h' para el nombre de host, en la variable.

Por ejemplo, aquí está mi indicador de bash que usa tanto PROMPT_COMMAND como PS1. El código bash en PROMPT_COMMAND determina en qué rama de git podría estar y lo muestra en el indicador, junto con el estado de salida del último proceso ejecutado, el nombre de host y el nombre de base del pwd. La variable RET almacena el valor de retorno del último programa ejecutado. Esto es conveniente para ver si hubo un error y el código de error del último programa que ejecuté en la terminal. Tenga en cuenta el exterior 'que rodea toda la expresión PROMPT_COMMAND. Incluye PS1 para que esta variable se vuelva a evaluar cada vez que se evalúe la variable PROMPT_COMMAND.

PROMPT_COMMAND='RET=$?;\
  BRANCH="";\
  ERRMSG="";\
  if [[ $RET != 0 ]]; then\
    ERRMSG=" $RET";\
  fi;\
  if git branch &>/dev/null; then\
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2);\
  fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'

La salida de ejemplo se ve así en un directorio que no es de git:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 

y en un directorio de git ves el nombre de la rama:

sashan@dhcp-au-122 rework mybranch $ 

Actualizar

Después de leer los comentarios y la respuesta de Bob, creo que es mejor escribirlo como él lo describe. Es más fácil de mantener que lo que escribí originalmente anteriormente, donde la variable PS1 se establece dentro de PROMPT_COMMAND, que en sí misma es una cadena súper complicada que bash evalúa en tiempo de ejecución. Funciona, pero es más complicado de lo necesario. Para ser justos, escribí PROMPT_COMMAND para mí hace unos 10 años y funcionó y no pensé demasiado en ello.

Para aquellos curiosos sobre cómo modifiqué mis cosas, básicamente puse el código para PROMPT_COMMAND en un archivo separado (como Bob describió) y luego repitió la cadena que pretendo que sea PS1:

GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour

if [ -z $SCHROOT_CHROOT_NAME ]; then
    SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
    ERRMSG=" $RET"
fi
if which git &>/dev/null; then
    BRANCH=$(git branch 2>/dev/null | grep \* |  cut -d " " -f 2)
else
    BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"

y en mi .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
Sashang
fuente
1
Se podría acortar una de sus líneas: if git branch &>/dev/null ; then\ . Redirige tanto stdout como stderr a / dev / null. tldp.org/LDP/abs/html/io-redirection.html
3
No es necesario exportar PROMPT_COMMAND .
Dolmen
2
Creo que de ceving comentario es muy cierto para esta respuesta también:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
No veo una razón por la cual cambiar en PS1línea dentro de PROMPT_COMMANDes desventajoso. Es un código útil perfecto. En contraste con la respuesta de Bob, la PS1variable se construyó correctamente. Esto permite un indicador de bash mucho más sofisticado dependiendo de su situación real.
Christian Wolf
2
La construcción de @ChristianWolf del PS1interior PROMPT_COMMANDno sirve para nada. es un ejemplo de cómo no hacerlo. construya PS1una vez .bash_profile, utilice comillas simples en lugar de comillas dobles, de modo que las sustituciones de variables se evalúen durante cada solicitud.
pal
46

La diferencia es que PS1 es la cadena de solicitud real utilizada y PROMPT_COMMAND es un comando que se ejecuta justo antes de la solicitud. Si desea la forma más sencilla y flexible de crear un mensaje, intente esto:

Pon esto en tu .bashrc:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command

Luego escriba un script (bash, perl, ruby: su elección) y colóquelo en ~ / bin / bash_prompt.

El guión puede usar cualquier información que desee para construir un mensaje. En mi opinión, esto es mucho más simple porque no tienes que aprender el lenguaje de sustitución algo barroco que se desarrolló solo para la variable PS1.

Podría pensar que podría hacer lo mismo simplemente configurando PROMPT_COMMAND directamente en ~ / bin / bash_prompt y configurando PS1 en la cadena vacía. Al principio, esto parece funcionar, pero pronto descubre que el código de línea de lectura espera que PS1 se establezca en el indicador real, y cuando se desplaza por las contraseñas en el historial, las cosas se complican como resultado. Esta solución temporal hace que PS1 siempre refleje el indicador más reciente (ya que la función establece el PS1 real utilizado por la instancia de invocación del shell), y esto hace que la línea de lectura y el historial de comandos funcionen bien.

Beto
fuente
17
No configure PS1en PROMPT_COMMAND! Establezca variables PROMPT_COMMANDy utilícelas en PS1. De lo contrario, perderá la capacidad de usar las PS1secuencias de escape como \uo \h. Tienes que reinventarlos PROMPT_COMMAND. Eso podría ser posible, pero no es posible evitar la pérdida de \[y \]que marcan el principio y el final de los caracteres no imprimibles. Esto significa que no puede usar colores sin confundir al terminal sobre la longitud del mensaje. Y esto confunde readlineal editar un comando que genera dos líneas. Al final tienes un gran lío en la pantalla.
hasta el
1
@ceving Es cierto que! Uno puede usar PROMPT_COMMAND para cambiar el formato de su PS1 y obtener lo mejor de ambos mundos
2grit
3
PROMPT_COMMANDse ejecuta antes de imprimir PS1. No veo ningún problema de configuración PS1desde el interior PROMPT_COMMAND, porque una vez PROMPT_COMMANDfinalizado, se imprimirá el shell PS1, que se modificó desde PROMPT_COMMAND(o en este caso, desde dentro prompt_command)?
Felipe Alvarez
3
Advertencia: PROMPT_COMMAND generalmente no debe usarse para imprimir caracteres directamente en el indicador. Bash no cuenta los caracteres impresos fuera de PS1, lo que provocará que coloque el cursor de forma incorrecta y borre los caracteres. Utilice PROMPT_COMMAND para configurar PS1 o observe los comandos de incrustación. ( Fuente Arch Wiki )
meffect
3
No entiendo por qué todos intentan hacer algunos trucos en PROMPT_COMMAND en lugar de simplemente usar la sustitución de comandos en PS1 export PS1='$(~/bin/bash_prompt)'hace lo mismo que el error se ve cuerdo
amigo
10

De man bash:

PROMPT_COMMAND

Si se establece, el valor se ejecuta como un comando antes de emitir cada indicador principal.

PS1

El valor de este parámetro se expande (consulte PROMPTING a continuación) y se utiliza como la cadena de solicitud principal. El valor predeterminado es '' \ s- \ v \ $ ''.

Si simplemente desea establecer la cadena de solicitud, usar PS1solo es suficiente:

PS1='user \u on host \h$ '

Si desea hacer algo más justo antes de imprimir el mensaje, utilice PROMPT_COMMAND. Por ejemplo, si desea sincronizar las escrituras en caché en el disco, puede escribir:

PROMPT_COMMAND='sync'
Cyker
fuente
1
También puede establecer el título del terminal desde PS1sin necesidad PROMPT_COMMAND, ya que la secuencia que establece el título se puede incluir en PS1envuelto con \[y \].
dolmen
1
@dolmen Está bien. Luego hagamos otra cosa, como establecer dinámicamente una variable de entorno.
Cyker
@Cyker puede configurar dinámicamente la variable de entorno PS1, simplemente se configurará en la subcapa, por lo que no puede recuperar su valor. pero su ejemplo es trivialPS1='$(sync)user \u on host \h$ '
pal
1

la diferencia es que

  • si genera una línea incompleta desde PROMPT_COMMAND, arruinará su indicador de bash
  • PS1sustitutos \Hy amigos
  • PROMPT_COMMANDejecuta su contenido, PS1utiliza su contenido como indicador.

PS1realiza la expansión de variables y la sustitución de comandos en cada indicador, no es necesario utilizarlo PROMPT_COMMANDpara asignar un valor PS1o ejecutar código arbitrario. puedes hacerlo fácilmente export PS1='$(uuidgen) $RANDOM'una vez .bash_profile, solo usa comillas simples

camarada
fuente
0

Sí, para tratar de concretar esto:

  • PROMPT_COMMANDEs una función / variable de conveniencia útil de bash , pero no hay, estrictamente hablando, nada que no se pueda hacer usando PS1solo, ¿correcto?

Quiero decir, si uno quiere establecer otra variable con alcance fuera del indicador: dependiendo del shell, esa variable probablemente deba declararse primero fuera $PS1o (en el peor de los casos) uno podría tener que ponerse elegante con algo esperando en un FIFO antes de llamando $PS1(y armado de nuevo al final de $PS1); el \u \hpodría causar algunos problemas, particularmente si está usando una expresión regular elegante; pero de lo contrario: ¿se puede lograr cualquier cosa PROMPT_COMMANDmediante el uso de la sustitución de comandos dentro $PS1(y, tal vez en casos de esquina, subcapas explícitas)?

¿Correcto?

Geoff Nixon
fuente