Como incluir comandos no PS1 do Bash sem quebrar o cálculo do comprimento da linha?

13

Tonin apontou um erro no meu prompt padrão . Exemplo mínimo:

  1. Defina PS1:

    PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s $(tput setaf 1) $exit_code $(tput sgr0) " ")$ '

    Nesse ponto, o prompt se parece com o seguinte:

    $ 
  2. Agora, ative a saída do código de saída executando:

    false

    Agora, o prompt contém o código de saída em vermelho no início da linha:

    1 $ 
  3. Pressione Ctrl- r.
  4. Digite "false". Agora, o prompt contém apenas a pesquisa:

    (reverse-i-search)`false': false
  5. Pressione Enter.

O histórico do terminal resultante agora contém o seguinte:

1 $ch)`false': false

Saída esperada:

1 $ false

Ou seja, parece que a saída da pesquisa de histórico é misturada ao prompt e oculta o comando real que foi executado.

Tentei contornar isso usandoPROMPT_COMMAND :

set_exit_code() {
    exit_code=$?
    [[ $exit_code -eq 0 ]] || printf %s $(tput setaf 1) $exit_code $(tput sgr0) " "
}
set_bash_prompt() {
    PS1='$(set_exit_code)$ ' # Double quotes give the same result
}
PROMPT_COMMAND=set_bash_prompt

Isso não parece funcionar - a linha parece exatamente a mesma de antes, depois de pesquisar e executar.

Como posso consertar isso?

l0b0
fonte
1
Esta parece ser a continuação de unix.stackexchange.com/a/71012
manatwork

Respostas:

8

Encontrei a resposta no askubuntu.com . @qeirha mencionou que você precisa informar ao bash que a sequência de caracteres não deve ser contada no comprimento do prompt, e você faz isso colocando-o dentro \[ \]. Com base no exemplo fornecido, aqui está uma solução:

red=$(tput setaf 1)

reset=$(tput sgr0)

[ "$PS1" = "\\s-\\v\\\$ " ] && PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s \[$red\] $exit_code \[$reset\] " ")$ '
Timothy Martin
fonte
Não é necessário ir ao Ask Ubuntu . Já temos respostas suficientes para essa pergunta aqui também.
Manatwork
Obrigado pelo conselho @manatwork! Queria dar o devido crédito pela explicação e forneci a referência como cortesia.
Timothy Martin
Dar crédito não é um problema. Mas, enquanto falava sobre o problema: as barras invertidas sem escape costumavam desaparecer do Markdown, de modo que o seu plain \ [se tornou [no seu post, portanto o código exibido não estava funcional, copiando-o no terminal. Isso pode ser evitado usando código embutido ou marcação de bloco de código. ( Como faço para formatar meus posts usando Markdown ou HTML? )
manatwork
1
D'oh! Eu já corrigi o mesmo problema para outro PS1código. Por que não o vi?
L0b0
1
PS1='$(exit_code=$?; [[ $exit_code -eq 0 ]] || printf %s \[$(tput setaf 1)\] $exit_code \[$(tput sgr0)\] " ")$ '

(Desculpe, não há explicação aqui. Consulte Como personalizar o PS1 corretamente? Ou qualquer outra pergunta sobre problemas de cálculo de tamanho de prompt e \[... \]).

manatwork
fonte
Na segunda questão @ l0b0, adicionarei isso usando o PS1 e \[...\]funcionará bem, desde que você possa colocar todo o código que deseja gerar seu prompt em uma única string. No entanto, se você deseja dividir seu código em pequenas funções, chega a um ponto em que não pode colocar os colchetes inicial e final na mesma string / função. E isso quebra quebra de linha. A menos que você recorra ao recurso PROMPT_COMMANDpara recalcular o seu PS1a cada prompt.
Tonin
1

Expandindo a resposta @manatwork, mas mantendo seu código dividindo a PS1computação em diferentes funções, você pode escrever sua solicitação da seguinte maneira:

set_exit_code() {
    exit_code=$?
    [[ $exit_code -eq 0 ]] || printf "\[$(tput setaf 1)\] $exit_code \[$(tput sgr0)\] "
}
set_bash_prompt() {
    PS1="$(set_exit_code)$ " # with double quotes!
}
PROMPT_COMMAND=set_bash_prompt

As aspas duplas são obrigatórias ao definir PS1e ao usar printfna função.

Tonin
fonte
Para referência futura, use uma função bash no seu .bashrc- não coloque código em um arquivo separado e chame isso.
starbeamrainbowlabs