Escapar caracteres não imprimíveis em uma função para um prompt do Bash

22

Em um prompt bash (variável PS1), estou chamando uma função para potencialmente adicionar texto ao prompt: export PS1="\u@\h \$(my_function) \$ "

No entanto, a função no prompt contém códigos de cores ANSI que são alterados com base na saída da função (às vezes vermelho, às vezes verde). Adicionar " \[" à variável PS1 deve escapar desses códigos como não imprimíveis, mas se eu fizer um echona função, o " \[" será impresso literalmente no prompt.

Como posso escapar desses códigos de cores ANSI de uma função para uso em um prompt do bash?

Meia-noite
fonte

Respostas:

34

A biblioteca readline aceita \001e \002(ASCII SOH e STX ) como delimitadores de texto não imprimíveis. Eles também funcionam em qualquer aplicativo que use o readline .

From lib/readline/display.c:243no código-fonte do bash :

243 /* Current implementation:
244         \001 (^A) start non-visible characters
245         \002 (^B) end non-visible characters
246    all characters except \001 and \002 (following a \001) are copied to
247    the returned string; all characters except those between \001 and
248    \002 are assumed to be `visible'. */

O bash é específico \[e \], de fato, é traduzido para \001e \002em y.tab.c:7640.


Nota: Se você usar bash 's printfou echo -e, e se o seu texto tiver \001ou \002imediatamente antes de um número , ocorrerá um bug do bash que faz com que ele coma um dígito a mais ao processar escapes octais - ou seja, \00142será interpretado como octal 014 (seguido por ASCII "2"), em vez do octal correto 01 (seguido por ASCII "42"). Por esse motivo, use versões hexadecimais \x01e, em \x02vez disso.

gravidade
fonte
Isso mesmo! echo -e "\001\e[31m\002RED"funciona como esperado. Obrigado!
MidnightLightning
Desculpe ressuscitar uma resposta, mas qual é o equivalente em dash / ash / sh?
Hosh Sadiq
@ Josh Se eles usarem o readline \001e \002funcionarão. Caso contrário, não tenho certeza. O Dash, por exemplo, definitivamente não usa o readline .
wjandrea
1

Aqui está uma boa resposta completa. Eu tive que fazer muito mais escavações para descobrir aonde os \ 001 etc. tinham que ir. Espero que isto ajude.

# Color prompt for git
reset=$(tput sgr0)
boldgreen=$(tput setaf 2)$(tput bold)
cyan=$(tput sgr0)$(tput setaf 6)
boldred=$(tput setaf 1)$(tput bold)
boldwhite=$(tput setaf 7)$(tput bold)
boldyellow=$(tput setaf 3)$(tput bold)

PARENCLR=$'\001\e[0;36m\002'
BRANCHCLR=$'\001\e[1;33m\002'

alias branchname="git branch 2>/dev/null | grep '*' | sed 's/* \(.*\)/ ${PARENCLR}(${BRANCHCLR}\1${PARENCLR}\)/'"

GIT_STATUS='$(branchname)'

PROMPT_CHAR="\$"
PS1="\[$boldgreen\]\u\[$cyan\]::\[$boldred\]\h \[$cyan\]{\[$boldwhite\].../\W\[$cyan\]}\[$reset\]$GIT_STATUS\[$reset\]$PROMPT_CHAR "

Do jeito que eu o configurei aqui, os parênteses do ramo git aparecerão apenas se você estiver em um ramo git, caso contrário, ficará em branco.

Dan L
fonte
0

Com base na resposta do grawity , o seguinte incluirá sequências de controle ANSI em ASCII SOH( ^A) e STX( ^B) que são equivalentes \[e, \]respectivamente:

function readline_ANSI_escape() {
  if [[ $# -ge 1 ]]; then
    echo "$*"
  else
    cat  # Read string from STDIN
  fi | \
  perl -pe 's/(?:(?<!\x1)|(?<!\\\[))(\x1b\[[0-9;]*[mG])(?!\x2|\\\])/\x1\1\x2/g'
}

Use-o como:

$ echo $'\e[0;1;31mRED' | readline_ANSI_escape

Ou:

$ readline_ANSI_escape "$string"

Como bônus, executar a função várias vezes não escapará novamente aos códigos de controle que já escaparam.

Tom Hale
fonte
-2

Se você quiser usá-los no prompt, precisará fazer o \[. Mas se você quiser usá-lo em um eco, precisará usá-lo \033[.

Wuffers
fonte
Hmmm ... Adicionando \ 033 [antes do comando ANSI ("\ e [31m") e \ 033] depois que parece criar o próximo caractere impresso no prompt, não é impresso.
MidnightLightning
11
Você não quer fazer \ 033] depois disso. \ 033 [31m inicia a cor; depois disso, é necessário
restaurá