Defina o código de saída do último comando para o prompt do bash

2

Estou executando o Ubuntu 18.04 e gostaria de definir meu prompt bash para que ele seja lido da seguinte forma:

user:~/Documents [14:22:07] 1 $

Meu PS1 é o seguinte:

ALERT_COLOR="$(tput setaf 1)"

# Display unsuccessful exit codes
function exit_status {
    last_status=$?
    if [[ $last_status != 0 ]]; then
        echo "$ALERT_COLOR[$last_status]"
    fi  
}

parse_git_branch() {
 git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/()/'
}
if [ "$color_prompt" = yes ]; then
 PS1='${debian_chroot:+($debian_chroot)}\[3[01;32m\]\u:\[3[01;34m\]\w\[3[01;31m\]$(parse_git_branch)\[3[00m\] \[3[36m\][\t] \[$ALERT_COLOR\]$exit_status \[3[0;37m\]\$ '
else
 PS1='${debian_chroot:+($debian_chroot)}\u:\w$(parse_git_branch)\$ '
fi
unset color_prompt force_color_prompt

Tudo funciona à parte do código de saída. Ele nunca exibe o código de saída, e se eu fizer apenas $? no meu PS1, ele sempre será mostrado como 0

    
por Luke 19.09.2018 / 15:26

2 respostas

6

A questão está aqui:

PS1='...\[$ALERT_COLOR\]$exit_status ...\$ '
                        ^^

Isso é uma expansão de parâmetro, ele não chama a função que você configurou. Você precisa chamar a função dentro de uma substituição de comando, por exemplo $(exit_status) ou de PROMPT_COMMAND . Se você fizer isso, tome cuidado com o \[ .. \] escapes: O Bash os interpreta antes de outras expansões no prompt, então você tem que codificá-los na string de prompt (eles não podem ser partes de variáveis ou outras coisas expandidas no prompt).

E se não expandir o prompt de escape de variáveis parece para trás para você, não posso culpá-lo. Mas é assim que é documentado :

In addition, the following table describes the special characters which can appear in the prompt variables PS1 to PS4: [...] After the string is decoded, it is expanded via parameter expansion, command substitution, [...]

Algo como isso deve funcionar:

normal_color=$'3[00m'
red_color=$'3[41m'
exit_color=$normal_color

set_exit_color() {
    if [ "$?" != 0 ]; then
        exit_color=$red_color
    else
        exit_color=$normal_color
    fi
}

PROMPT_COMMAND=set_exit_color
PS1='\[$exit_color\][$?]\[$normal_color\] \w\$ '

Eu teria pensado que precisaria de uma variável temporária para manter o status de saída, mas aparentemente PROMPT_COMMAND não modifica o valor de $? que é expandido no prompt. Se chamar a função com uma substituição de comando de dentro da cadeia de prompt, você precisará de uma solução alternativa, pois o código de saída da substituição de comando entrará em vigor. Algo assim:

normal_color=$'3[00m'
red_color=$'3[41m'
exit_color=$normal_color

exit_color() {
    exit_code=$?
    if [ "$exit_code" != 0 ]; then
        echo "$red_color"
    else
        echo "$normal_color"
    fi
    return "$exit_code"
}

PS1='\[$(exit_color)\][$?]\[$normal_color\] \w\$ '

Eu usaria a versão com PROMPT_COMMAND , apenas para salvar o fork do subshell causado pela substituição do comando, mas na prática o efeito é mínimo.

    
por 19.09.2018 / 15:36
1

Eu gosto de ter um prompt de 3 linhas, então eu faço isso:

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 10:56:52
jackman@jackmanVM:~/tmp
+$ cat ~/.bash_prompt
# ... some other stuff ...

__bash_prompt() {
    local last_status=$1
    local cwd=$( sed "s,^$HOME,~," <<<"$PWD" )
    local user_host_path="${debian_chroot:+($debian_chroot) }$(id -un)@$(hostname -s):$cwd"

    # terminal title
    echo -ne "\e]0;$user_host_path\a"

    # separator and date
    local char="۰"
    printf "%s bash %s " "$(jot -s "" -b "$char" $(( $(tput cols) - 18 )) )" "$char$char"
    date '+%T'

    if ((last_status != 0)); then
        local color_bold='\e[0;1m'
        local color_reset='\e[0m'
        printf "$color_bold[%d]$color_reset " $last_status
    fi

    # user@host, directory, git branch
    printf "%s%s\n" "${user_host_path}$(git_current_branch " (%s)")"
}

# ref: https://stackoverflow.com/questions/1039713/different-bash-prompt-for-different-vi-editing-mode
# a single-line PS1 allows the show-mode-in-prompt inputrc setting to be useful
PROMPT_COMMAND='__bash_prompt $?'
PS1='\$ '

Eu obtenho esse arquivo no meu ~ / .bashrc

O status de saída é exibido assim

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 11:05:10
jackman@jackmanVM:~/tmp
+$ sh -c 'exit 42'
۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 11:05:14
[42] jackman@jackmanVM:~/tmp
+$ false
۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 11:05:16
[1] jackman@jackmanVM:~/tmp
+$ true
۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 11:05:17
jackman@jackmanVM:~/tmp
+$ 

Note que a primeira coisa que faço na função é armazenar o último status de saída, antes de executar qualquer outro comando

Eu uso o PROMPT_COMMAND para produzir todas as coisas extravagantes e tenho apenas um PS1 simples, porque eu uso o modo vi e gosto de ver o indicador do modo vi. Eu também tenho isso:

۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰۰ bash ۰۰ 10:57:21
jackman@jackmanVM:~/tmp
+$ cat ~/.inputrc
set editing-mode vi

set show-mode-in-prompt on
$if Bash
    # not until bash 4.4, I believe
    # escape sequences: https://stackoverflow.com/a/42107711/7552 
    #set vi-ins-mode-string "+\e[5 q"
    #set vi-cmd-mode-string ":\e[1 q"
    set vi-ins-mode-string +
    set vi-cmd-mode-string :
$endif

Eu uso como meu shell de login e, ocasionalmente, uso o ksh , então eu coloquei o nome do shell no separador de prompt para que eu possa acompanhar o que estou digitando.

    
por 19.09.2018 / 17:07