Qual é a diferença entre PS1 e PROMPT_COMMAND

108

Ao dar uma olhada neste tópico incrível , percebi que alguns exemplos usam

PS1="Blah Blah Blah"

e alguns usam

PROMPT_COMMAND="Blah Blah Blah"

(e alguns usam ambos) ao definir o prompt em um shell bash. Qual é a diferença entre os dois? Uma busca SO e até mesmo uma busca mais ampla no Google não estão me trazendo resultados, então mesmo um link para o lugar certo para procurar a resposta seria apreciado.

Jed Daniels
fonte

Respostas:

59

Da página de doc do 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).

Eu nunca usei, mas poderia ter usado quando eu só tinha sh.

Scott Thomson
fonte
67

PROMPT_COMMAND pode conter instruções bash comuns, enquanto a variável PS1 também pode conter os caracteres especiais, como '\ h' para o nome do host, na variável.

Por exemplo, aqui está meu prompt bash que usa PROMPT_COMMAND e PS1. O código bash em PROMPT_COMMAND calcula em qual branch git você está e o exibe no prompt, junto com o status de saída do último processo executado, nome do host e nome de base do pwd. A variável RET armazena o valor de retorno do último programa executado. Isso é conveniente para ver se houve um erro e o código de erro do último programa que executei no terminal. Observe o 'externo em torno de toda a expressão PROMPT_COMMAND. Inclui PS1 para que esta variável seja reavaliada cada vez que a variável PROMPT_COMMAND for avaliada.

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";'

A saída de exemplo fica assim em um diretório não git:

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

e em um diretório git você vê o nome do branch:

sashan@dhcp-au-122 rework mybranch $ 

Atualizar

Depois de ler os comentários e a resposta de Bob, acho que é melhor escrever como ele descreve. É mais sustentável do que o que escrevi originalmente acima, onde a variável PS1 é definida dentro do PROMPT_COMMAND, que por si só é uma string supercomplicada que é avaliada em tempo de execução pelo bash. Funciona, mas é mais complicado do que precisa ser. Para ser justo, eu escrevi esse PROMPT_COMMAND para mim há cerca de 10 anos e funcionou e não pensei muito sobre isso.

Para aqueles que estão curiosos para saber como eu alterei minhas coisas, basicamente coloquei o código para o PROMPT_COMMAND em um arquivo separado (como Bob descreveu) e, em seguida, ecoa a string que pretendo ser 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"

e no meu .bashrc

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
Sashang
fonte
1
Você pode encurtar uma das suas linhas: if git branch &>/dev/null ; then\ . Ele redireciona stdout e stderr para / dev / null. tldp.org/LDP/abs/html/io-redirection.html
3
Não há necessidade de exportar PROMPT_COMMAND .
Dolmen de
2
Acho que o comentário de ceving é muito verdadeiro para esta resposta também:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1
phil294
2
Não vejo uma razão pela qual mudar PS1online dentro PROMPT_COMMANDé desvantajoso. É um código perfeitamente útil. Em contraste com a resposta de Bob para baixo, a PS1variável foi construída corretamente. Isso permite um prompt bash muito mais sofisticado, dependendo da sua situação real.
Christian Wolf de
2
A construção PS1interna de @ChristianWolf PROMPT_COMMANDnão serve para nada. é um exemplo de como não fazer. construir PS1uma vez .bash_profile, apenas use aspas simples em vez de aspas duplas, para que as substituições de variáveis ​​sejam avaliadas durante cada prompt.
amigo de
46

A diferença é que PS1 é a string de prompt real usada e PROMPT_COMMAND é um comando executado logo antes do prompt. Se você deseja a maneira mais simples e flexível de construir um prompt, tente o seguinte:

Coloque isso em seu .bashrc:

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

Em seguida, escreva um script (bash, perl, ruby: sua escolha) e coloque-o em ~ / bin / bash_prompt.

O script pode usar qualquer informação que desejar para construir um prompt. Isso é IMO muito mais simples porque você não precisa aprender a linguagem de substituição um tanto barroca que foi desenvolvida apenas para a variável PS1.

Você pode pensar que poderia fazer o mesmo simplesmente definindo PROMPT_COMMAND diretamente para ~ / bin / bash_prompt e definindo PS1 para a string vazia. A princípio, isso parece funcionar, mas você logo descobre que o código readline espera que o PS1 seja configurado para o prompt real e, quando você rola as palavras para trás no histórico, as coisas ficam confusas como resultado. Essa solução alternativa faz com que o PS1 sempre reflita o prompt mais recente (uma vez que a função define o PS1 real usado pela instância de chamada do shell), e isso faz com que a linha de leitura e o histórico de comandos funcionem bem.

Prumo
fonte
17
Não instale PS1em PROMPT_COMMAND! Defina variáveis ​​em PROMPT_COMMANDe use-as em PS1. Caso contrário, você perderá a capacidade de usar as PS1sequências de escape como \uou \h. Você tem que reinventá-los PROMPT_COMMAND. Isso pode ser possível, mas não é possível contornar a perda \[e \]que marcam o início e o fim dos caracteres não imprimíveis. Isso significa que você não pode usar cores sem confundir o terminal sobre o comprimento do prompt. E isso confunde readlineao editar um comando que gera duas linhas. No final, você tem uma grande bagunça na tela.
início de
1
@ceving É verdade! Pode-se usar PROMPT_COMMAND para alterar o formato de seu PS1 e obter o melhor dos dois mundos
2grit
3
PROMPT_COMMANDé executado antes da impressão PS1. Não vejo problemas de configuração PS1por dentro PROMPT_COMMAND, pois depois de PROMPT_COMMANDfinalizado, o shell irá imprimir PS1, o que foi modificado PROMPT_COMMAND(ou neste caso, por dentro prompt_command)?
Felipe Alvarez
3
Aviso: PROMPT_COMMAND geralmente não deve ser usado para imprimir caracteres diretamente no prompt. Os caracteres impressos fora do PS1 não são contados pelo Bash, o que fará com que ele posicione incorretamente o cursor e apague os caracteres. Use PROMPT_COMMAND para definir PS1 ou observe os comandos de incorporação. ( Fonte do Arch Wiki )
meffect
3
Eu não entendo porque todo mundo tenta fazer alguns truques em PROMPT_COMMAND em vez de apenas usar a substituição de comando no PS1 export PS1='$(~/bin/bash_prompt)'faz a mesma coisa bug parece normal
amigo
10

De man bash:

PROMPT_COMMAND

Se definido, o valor é executado como um comando antes de emitir cada prompt primário.

PS1

O valor deste parâmetro é expandido (consulte PROMPTING abaixo) e usado como a string de prompt principal. O valor padrão é '' \ s- \ v \ $ ''.

Se você simplesmente deseja definir a string do prompt, usar PS1sozinho é o suficiente:

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

Se quiser fazer outra coisa antes de imprimir o prompt, use PROMPT_COMMAND. Por exemplo, se você deseja sincronizar gravações em cache para o disco, pode escrever:

PROMPT_COMMAND='sync'
Cyker
fonte
1
Você também pode definir o título do terminal PS1sem a necessidade PROMPT_COMMAND, pois a sequência que define o título pode ser incluída PS1entre \[e \].
Dolmen
1
@dolmen Tudo bem. Então, vamos fazer outra coisa, como definir dinamicamente uma variável de ambiente.
Cyker
@Cyker você pode definir dinamicamente a variável de ambiente PS1, ela apenas será definida no subshell, então você não pode obter seu valor de volta. mas seu exemplo é trivialPS1='$(sync)user \u on host \h$ '
amigo de
1

a diferença é que

  • se você enviar uma linha incompleta de PROMPT_COMMAND, irá estragar seu prompt bash
  • PS1substitutos \He amigos
  • PROMPT_COMMANDexecuta seu conteúdo, PS1usa seu conteúdo como prompt.

PS1faz expansão de variável e substituição de comando em cada prompt, sem necessidade de usar PROMPT_COMMANDpara atribuir valor PS1ou executar código arbitrário. você pode fazer facilmente export PS1='$(uuidgen) $RANDOM'uma vez .bash_profile, basta usar aspas simples

amigo
fonte
0

Sim, para tentar realmente acertar:

  • PROMPT_COMMANDé uma função / variável de conveniência bash útil , mas não há, estritamente falando, nada que também não possa ser feito PS1sozinho, correto?

Quero dizer, se alguém deseja definir outra variável com escopo fora do prompt: dependendo do shell, essa variável provavelmente precisaria ser declarada primeiro fora $PS1ou (pior caso), pode-se ter que imaginar algo esperando em um FIFO antes de chamando $PS1(e armado novamente no final de $PS1); o \u \hpode causar alguns problemas, especialmente se você estiver usando alguma fantasia regex; mas caso contrário: pode-se realizar qualquer coisa PROMPT_COMMANDusando a substituição de comando dentro $PS1(e, talvez em casos extremos, subshells explícitos)?

Certo?

Geoff Nixon
fonte