Bash: Exibe o status de saída no prompt:

11
GREEN="\e[1;32m"
RED="\e[1;31m"
NONE="\e[m"

get_exit_status(){
   es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}${NONE}"
   else
       echo -e "${RED}${es}${NONE}"
   fi
}

get_path(){
    #dummy function
    echo "PATH"
}

PROMPT_COMMAND='exitStatus=$(get_exit_status)'

A seguir, é apresentado o exitStatus correto, mas as variáveis ​​de cores não são expandidas:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

No entanto, o abaixo me fornece as cores, mas o status de saída não é atualizado:

PS1="${RED}\h $(get_path) ${exitStatus}${NONE} "

Qual é a maneira certa de fazer isso? Como posso corrigir isso para que o exitStatus e as cores funcionem?

dogbane
fonte

Respostas:

8

Gilles identificou seu principal problema, mas eu queria tentar explicá-lo de maneira diferente.

O Bash interpreta que o prompt especial escapa apenas antes de expandir quaisquer variáveis ​​no prompt. Isso significa que o uso \ede uma variável expandida a partir do prompt não funciona, mesmo que funcione diretamente PS1.

Por exemplo, isso funciona conforme o esperado e fornece texto em vermelho:

PS1='\e[1;31m this is in red '

Mas isso não acontece, apenas coloca um literal \eno prompt:

RED='\e[1;31m'
PS1="$RED not in red "

Se você deseja armazenar as fugas de cor nas variáveis, é possível usar ANSI-C entre aspas ( $'...') para colocar um caractere de escape literal na variável.

Para fazer isso, você pode mudar a sua definição de GREEN, REDe NONE, por isso, o seu valor é a seqüência de escape real.

GREEN=$'\033[1;32m'
RED=$'\033[1;31m'
NONE=$'\033[m'

Se você fizer isso, o primeiro PS1com aspas simples deve funcionar:

PS1='${RED}\h $(get_path) ${exitStatus}${NONE} '

No entanto, você terá um segundo problema.

Tente executar isso, pressione e Up Arrow, em seguida Home, e seu cursor não retornará ao início da linha.

Para corrigir isso, altere PS1para incluir \[e \]contornar as seqüências de escape de cores, por exemplo

PS1='\[${RED}\]\h $(get_path) $?\[${NONE}\] '

Você não pode usá-lo get_exit_statuscorretamente aqui, pois a saída contém caracteres de impressão (o código de saída) e caracteres não imprimíveis (os códigos de cores), e não há como marcá-lo corretamente no prompt. A colocação \[...\]marcaria como não imprimível na íntegra, o que não está correto. Você precisará alterar a função para que ela imprima apenas o código de cor adequado e, em seguida, coloque-a \[...\]no prompt.

Mikel
fonte
\[é \1e \[é \2. Aqueles corresponde a algo de alguma linha de leitura RL_PROMPT_{START,END}_IGNOREque solicita que ele ignore os bytes ao contar o comprimento do prompt na tela. Consulte lists.gnu.org/archive/html/bug-bash/2015-08/msg00027.html .
Arthur2e5
@ Arthur2e5 Você quer dizer \]é \2? E você quer dizer que é por isso que é necessário ${exitStatus}? Meu ponto era que ${exitStatus}não contém caracteres não imprimíveis, então o bash deve ser capaz de determinar corretamente quantos caracteres ele se move o prompt sem a \[e \]no \[${exitStatus}\].
Mikel
O problema é que ele contém algumas - as cores. (ANSI Escapes)
Arthur2e5 27/10/2015
@ Arthur2e5 Ew, eu senti muita falta disso. :) Por que você colocaria cores ... não importa. :)
Mikel
1
"O Bash está efetivamente chamando eco no seu PS1, não eco-e" - bem, isso está errado ou está errado. O Bash expande escapes de barra invertida como \ee \033(e \[/ \], \ue \h) no prompt, apenas o faz antes de expandir as variáveis. Então PS1='\e[1;31m red'funciona, red='\e[1;31m'; PS1='$red red'não.
ilkkachu
3

Quando você executa PS1='${RED}\h $(get_path) ${exitStatus}${NONE} ', a PS1variável é definida como ${RED}\h $(get_path) ${exitStatus}${NONE}, onde apenas \hé uma sequência de escape rápida. Depois que as seqüências de prompt são expandidas (produzindo ${RED}darkstar $(get_path) ${exitStatus}${NONE}), o shell executa as expansões usuais, como expansões variáveis. Você recebe um prompt exibido que se parece \e[1;31mdarkstar PATH 0\e[m. Nada ao longo do caminho expande as \eseqüências para caracteres de escape reais.

Quando você executa PS1="${RED}\h $(get_path) ${exitStatus}${NONE} ", a PS1variável é definida como \e[1;31m\h PATH 0\e[m. As variáveis RED, exitStatuse NONEsão expandidos no momento da atribuição. Em seguida, a linha contém três sequências de escape rápidas ( \e, \h, e \ede novo). Não há variáveis ​​de shell para expandir neste estágio.

Para ver as cores, você precisa que as variáveis ​​de cores contenham caracteres de escape reais. Você pode fazer assim:

RED=$'\033[1;31m'
NONE=$'\033[m'
PS1='\[${RED}\]\h \w $?\[${NONE}\] '

$'…'expande sequências de barra invertida-octal e algumas seqüências de letra de barra invertida como \n, mas não incluindo \e. Fiz três outras alterações no seu prompt:

  • Use \[…\]em seqüências que não sejam de impressão, como comandos de mudança de cor. Caso contrário, sua tela acabará distorcida porque o bash não pode descobrir a largura do prompt.
  • \w é uma sequência de escape interna para imprimir o diretório atual.
  • Você não precisa de nada complicado para mostrar $?no prompt, se não tiver um PROMPT_COMMANDem primeiro lugar.
Gilles 'SO- parar de ser mau'
fonte
Penso que a ideia era tornar o alerta verde no sucesso e vermelho no fracasso.
mattdm
Sim, PS1é errado, mas o conselho para uso $'...'de REDe GREENdeve fazê-lo funcionar usando dogbane de PS1.
Mikel
1

Experimentar:

PS1='`exitStatus=$?;if [ $exitStatus -eq 0 ];then echo "\['${GREEN}'\]";else echo "\['${RED}'\]";fi;echo "\h $(get_path) ${exitStatus}${NONE}"`'
shellholic
fonte
1
Obrigado, isso funciona, mas existe alguma maneira de fazer isso sem ter que incorporar uma instrução if no prompt?
dogbane
1

Aqui está a abordagem que eu segui, evita o uso de PROMPT_COMMAND.

# This function is called from a subshell in $PS1,
# to provide a colourised visual indicator of the exit status of the last run command
__COLOURISE_EXIT_STATUS() {
    # uncomment the next line for exit code output after each command, useful for debugging and testing
    #printf -- "\nexit code: $1\n" >&2
    [[ 0 == "$1" || 130 == "$1" ]] && printf -- "$GREEN" || printf -- "$RED"
}

Então meu $PS1é o seguinte:

PS1='# ${debian_chroot:+($debian_chroot)}'"${GREEN}\u${YELLOW}@${DARK_YELLOW}\h${WHITE}:${LIGHT_BLUE}\w${WHITE}\n"'\[$(__COLOURISE_EXIT_STATUS $?)\]# \$'"\[${WHITE}\] "
Kyle
fonte
1
Embora isso não importe nesse caso específico, como o único valor que $?pode ter é um número inteiro, você realmente deve usá-lo printf '%b' "$GREEN". Além disso, evite usar nomes de funções prefixados com __ou _como eles são usados ​​pela conclusão do bash.
nyuszika7h
1

Aqui você vai - Isso funciona para mim (TM) no Ubuntu e em outros Linux (Linuxen?).

O motivo para colocar a detecção do código de saída $PS1é que um host possui um $PROMPT_COMMANDconjunto somente leitura antes da leitura do .bashrc.

l0b0
fonte
0

Por PROMPT_COMMANDisso, é mais fácil definir uma função e usar isso:

prompt_command() {
    # ...
}
PROMPT_COMMAND=prompt_command
nyuszika7h
fonte