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?

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.


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.


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.


-2

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


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á
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.