Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print escaped color characters to bash

Tags:

c

bash

readline

I'm using the readline library in C to create a bash-like prompt within bash. When I tried to make the prompt colorful, with color sequences like these, the coloring works great, but the cursor spacing is messed up. The input is wrapped around too early and the wrap-around is to the same line so it starts overwriting the prompt. I thought I should escape the color-sequences with \[ and \] like

readline("\[\e[1;31m$\e[0m\] ")

But that prints the square brackets, and if I escape the backslashes it prints those too. How do I escape the color codes so the cursor still works?

like image 557
ldanilek Avatar asked Dec 13 '15 12:12

ldanilek


2 Answers

The way to tell readline that a character sequence in a prompt string doesn't actually move the cursor when output to the screen is to surround it with the markers RL_PROMPT_START_IGNORE (currently, this is the character literal '\001' in readline's C header file) and RL_PROMPT_END_IGNORE (currently '\002').

And as @Joachim and @Alter said, use '\033' instead of '\e' for portability.

like image 132
Mark Plotnick Avatar answered Oct 29 '22 15:10

Mark Plotnick


I found this question when looking to refine the GNU readline prompt in a bash script. Like readline in C code, \[ and \] aren't special but \001 and \002 will work when given literally via the special treatment bash affords quoted words of the form $'string'. I've been here before (and left unsatisfied due to not knowing to combine it with $'…'), so I figured I'd leave my explanation here now that I have a solution.

Using the data provided here, I was able to conclude this result:

C1=$'\001\033[1;34m\002'   # blue   -  from \e[1;34m
C0=$'\001\033[0;0m\002'    # reset  -  from \e[0;0m
while read -p "${C1}myshell>$C0 " -e command; do
  echo "you said: $command"
done

This gives a blue prompt that says myshell> and has a trailing space, without colors for the actual command. Neither hitting Up nor entering a command that wraps to the next line will be confused by the non-printing characters.

As explained in the accepted answer, \001 (Start of Heading) and \002 (Start of Text) are the RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE markers, which tell bash and readline not to count anything between them for the purpose of painting the terminal. (Also found here: \033 is more reliable than \e and since I'm now using octal codes anyway, I might as well use one more.)

There seems to be quite the dearth of documentation on this; the best I could find was in perl's documentation for Term::ReadLine::Gnu, which says:

PROMPT may include some escape sequences. Use RL_PROMPT_START_IGNORE to begin a sequence of non-printing characters, and RL_PROMPT_END_IGNORE to end the sequence.

like image 37
Adam Katz Avatar answered Oct 29 '22 17:10

Adam Katz