Escape ANSI sequence of non-printing characters between \[ and \] - bash

The bash manual says that, in the prompt, any sequences of non-printing characters should be enclosed like: \[this\]:
\[ Begin a sequence of non-printing characters.
This could be used to embed a terminal control sequence into the prompt.
\] End a sequence of non-printing characters.
Given a string to be included in the prompt, how can i automatically escape all ANSI control / color codes, to make the prompt display / wrap correctly under all circumstances?
Differentiation: Here i assume that a string with ANSI control codes has already been produced.
This related question assumes that the delimiters can be inserted by editing the string's generating function.

The following will enclose ANSI control sequences in ASCII SOH (^A) and STX (^B) which are equivalent to \[ and \] respectively:
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 it like:
$ echo $'\e[0;1;31mRED' | readline_ANSI_escape
Or:
$ readline_ANSI_escape "$string"
As a bonus, running the function multiple times will not re-escape already escaped control codes.

Don't try to automate it.
The reason why Bash asks you to add the \[...\]'s manually is because the shell can't reasonably know how any given terminal will interpret any escape codes. If it was, Bash would just do it in the first place.
For example, here are a few of the many cases that the other answer fails to handle. In each case, no output is printed on my particular terminal and yet the escaping function fails to escape them:
printf '\e]1;Hello\a' # Set window title
printf '\e[0;2;0;0;0}' # 24-bit color
printf '\e[13]' # Unblank screen

Related

Escaping zero length characters in bash

Based on Bash color how-to I've tried to output a string in grey:
printf " \[\033[1;30m\]foo\[\033[0m\]"
What I get out though is: \[\]foo\[\]
According to the link above any zero width characters need to be surrounded by \[ and \] but it seems those characters are being output.
Any idea how to ensure the \[ is handled correctly?
As #chepner mentioned in his comment: the \[ sequences are used by some utilities, including PS1, to represent the \001 and \002 control codes:
\[ => \x01 or \001
\] => \x02 or \002
printf and echo don't do this translation from \[ to \001.
So the solution was to do the conversion myself. Instead of wrapping zero length characters in \[:
echo "\[\033[1;30m\]foo\[\033[0m\]"
which would output \[foo\]
I instead output the actual control code:
echo "\x01\033[1;30m\x02foo\x01\033[0m\x02"
which outputs foo on both PS1 printf and echo.
For a concrete example see this commit on git-radar.
printf "\033[1;30mfoo\033[0m"
is enough. \033[ (ESC[) starts the ANSI escape codes and m closes them.

Rewriting commands from history causes pieces of command and PS1 to be deleted and cursor to move unexpectedly [duplicate]

I'm using custom bash prompt to show git branch.
Everything is in /etc/bash/bashrc:
function formattedGitBranch {
_branch="$(git branch 2>/dev/null | sed -e "/^\s/d" -e "s/^\*\s//")"
# tried these:
echo -e "\e[0;91m ($_branch)"
echo -e "\e[0;91m ($_branch) \e[m"
echo -e $'\e[0;91m'"($_branch)"
echo "($_branch)"
echo "$(tput setaf 2) ($_branch) $(tput setaf 9)"
printf "\e[0;91m ($_branch)"
}
# color is set before function call
PS1='\[\033[01;34m\] \[\033[0;91m\]$(formattedGitBranch) \$\[\033[00m\] '
# color is set inside function
PS1='\[\033[01;34m\] $(formattedGitBranch) \$\[\033[00m\] '
Problem is that when I set color for $_branch in the function, my prompt will be overwritten when EOL is reached:
Tried all possible variants tput, printf, $'' notation.
I solved the problem by setting the colour only in PS1:
But..
I would like to know why it is overwriting my prompt
How to fix this issue when function is used
I'm using Gentoo Linux. GNU bash, verze 4.2.37(1)-release (i686-pc-linux-gnu)
1) I would like to know why it is overwriting my prompt
Because every non-printable characters have to be escaped by \[ and \] otherwise readline cannot keep track of the cursor position correctly.
You must put \[ and \] around any non-printing escape sequences in your prompt.
Without the \[ \] bash will think the bytes which constitute the escape sequences for the color codes will actually take up space on the screen, so bash won't be able to know where the cursor actually is.
\[ Begin a sequence of non-printing characters. (like color escape sequences). This
allows bash to calculate word wrapping correctly.
\] End a sequence of non-printing characters.
-- BashFAQ
...note the escapes for the non printing characters, these ensure that readline can keep track of the cursor position correctly. -- ss64.com
2) How to fix this issue when function is used
If you want to set colours inside a function whose output is used in PS you have two options.
Either escape the whole function call:
PS1='\[ $(formattedGitBranch) \] '
Or replace the non-printing Escape sequences inside echo. That is, replace:
\[ and \] with \001 \002
(thanks to user grawity!)
echo -e is not aware of bash's \[ \] so you have to substitute these with \001 & \002 ASCII control codes to delimit non-printable chars from printable:
function formattedGitBranch { echo -e "\001\e[0;91m\002 ($_branch)"; }
PS1='$(formattedGitBranch) '
Strings like \e[0;91m needs additional quoting, to prevent bash from calculating its length.
Enclose these strings from formattedGitBranch in \[ & \] as, \[\e[0;91m\]
You have done it correctly in other places. Just missed it in formattedGitBranch.
You have to take care of non printable character inside [\ and ] otherwise you might be getting cursor right on top of command prompt as shared in question itself , so I found something and just sharing it :-
For getting cursor after PS1 output on the same line :
few examples :
PS1='[\u#\h:\w]\$
PS1='[\[\033[0;32m\]\u#\h:\[\033[36m\]\W\[\033[0m\]]\$ '
Refer Link : syntax for bash PS1

How do you write a ESC character to be interpreted by echo?

Is there a \ESC or something I can put so echo command can interpret it?
Where is a list of these commands, like \t\n\r, etc?
If you're using bash, it's probably better to use that shell's so-called "ANSI-C Quoting". This lets you construct a string that you can pass to the echo command, which will simply print it.
For example, to print an ASCII ESC character:
echo $'\e'
or
echo -n $'\e'
if you don't want the trailing newline.
(The term "ANSI-C Quoting is a bit of a misnomer for several reasons. The C standard is currently produced by ISO, not ANSI, and the bash feature supports \e and \E to represent the Escape character, and \cX for control-X, features not in standard C. The handling of hex escapes is also a bit different.)
Even better, you can use the printf command, which also supports \e for the ESC character. printf is far more portable that echo. There are a number of different versions of the echo command, both as /bin/echo and as built-in commands in most shells. printf also exists in multiple versions, but the behavior across implementations is much more consistent. Code using printf is more likely to be portable to shells other than bash (or to older versions of bash) than code that depends on any of the more arcane features of bash's echo command, or of the GNU coreutils echo command.
Yes, you might have to pass the -e switch:
echo -e '\n\t\a'
you can find the list of interpreted escapes in echo manpage and info entry. This is for GNU coreutils echo, likely the one you are using:
If -e is in effect, the following sequences are recognized:
\\ backslash
\a alert (BEL)
\b backspace
\c produce no further output
\e escape
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\0NNN byte with octal value NNN (1 to 3 digits)
\xHH byte with hexadecimal value HH (1 to 2 digits)
Bash will replace /bin/echo with its own builtin, but all sequences above are interpreted.
Note that Posix compliance does not require this, so some shells will have partial or no support for the -e flag and escaping, namely Debian/Ubuntu Dash, which is the default shell on those platforms. You will have to invoke echo as /bin/echo to avoid calling the builtin and make sure your /bin/echo can handle escapes.
Use printf if you need to write portable code.

Custom Bash prompt is overwriting itself

I'm using custom bash prompt to show git branch.
Everything is in /etc/bash/bashrc:
function formattedGitBranch {
_branch="$(git branch 2>/dev/null | sed -e "/^\s/d" -e "s/^\*\s//")"
# tried these:
echo -e "\e[0;91m ($_branch)"
echo -e "\e[0;91m ($_branch) \e[m"
echo -e $'\e[0;91m'"($_branch)"
echo "($_branch)"
echo "$(tput setaf 2) ($_branch) $(tput setaf 9)"
printf "\e[0;91m ($_branch)"
}
# color is set before function call
PS1='\[\033[01;34m\] \[\033[0;91m\]$(formattedGitBranch) \$\[\033[00m\] '
# color is set inside function
PS1='\[\033[01;34m\] $(formattedGitBranch) \$\[\033[00m\] '
Problem is that when I set color for $_branch in the function, my prompt will be overwritten when EOL is reached:
Tried all possible variants tput, printf, $'' notation.
I solved the problem by setting the colour only in PS1:
But..
I would like to know why it is overwriting my prompt
How to fix this issue when function is used
I'm using Gentoo Linux. GNU bash, verze 4.2.37(1)-release (i686-pc-linux-gnu)
1) I would like to know why it is overwriting my prompt
Because every non-printable characters have to be escaped by \[ and \] otherwise readline cannot keep track of the cursor position correctly.
You must put \[ and \] around any non-printing escape sequences in your prompt.
Without the \[ \] bash will think the bytes which constitute the escape sequences for the color codes will actually take up space on the screen, so bash won't be able to know where the cursor actually is.
\[ Begin a sequence of non-printing characters. (like color escape sequences). This
allows bash to calculate word wrapping correctly.
\] End a sequence of non-printing characters.
-- BashFAQ
...note the escapes for the non printing characters, these ensure that readline can keep track of the cursor position correctly. -- ss64.com
2) How to fix this issue when function is used
If you want to set colours inside a function whose output is used in PS you have two options.
Either escape the whole function call:
PS1='\[ $(formattedGitBranch) \] '
Or replace the non-printing Escape sequences inside echo. That is, replace:
\[ and \] with \001 \002
(thanks to user grawity!)
echo -e is not aware of bash's \[ \] so you have to substitute these with \001 & \002 ASCII control codes to delimit non-printable chars from printable:
function formattedGitBranch { echo -e "\001\e[0;91m\002 ($_branch)"; }
PS1='$(formattedGitBranch) '
Strings like \e[0;91m needs additional quoting, to prevent bash from calculating its length.
Enclose these strings from formattedGitBranch in \[ & \] as, \[\e[0;91m\]
You have done it correctly in other places. Just missed it in formattedGitBranch.
You have to take care of non printable character inside [\ and ] otherwise you might be getting cursor right on top of command prompt as shared in question itself , so I found something and just sharing it :-
For getting cursor after PS1 output on the same line :
few examples :
PS1='[\u#\h:\w]\$
PS1='[\[\033[0;32m\]\u#\h:\[\033[36m\]\W\[\033[0m\]]\$ '
Refer Link : syntax for bash PS1

Echo tab characters in bash script

How do I echo one or more tab characters using a bash script?
When I run this code
res=' 'x # res = "\t\tx"
echo '['$res']' # expect [\t\tx]
I get this
res=[ x] # that is [<space>x]
echo -e ' \t '
will echo 'space tab space newline' (-e means 'enable interpretation of backslash escapes'):
$ echo -e ' \t ' | hexdump -C
00000000 20 09 20 0a | . .|
Use printf, not echo.
There are multiple different versions of the echo command. There's /bin/echo (which may or may not be the GNU Coreutils version, depending on the system), and the echo command is built into most shells. Different versions have different ways (or no way) to specify or disable escapes for control characters.
printf, on the other hand, has much less variation. It can exist as a command, typically /bin/printf, and it's built into some shells (bash and zsh have it, tcsh and ksh don't), but the various versions are much more similar to each other than the different versions of echo are. And you don't have to remember command-line options (with a few exceptions; GNU Coreutils printf accepts --version and --help, and the built-in bash printf accepts -v var to store the output in a variable).
For your example:
res=' 'x # res = "\t\tx"
printf '%s\n' "[$res]"
And now it's time for me to admit that echo will work just as well for the example you're asking about; you just need to put double quotes around the argument:
echo "[$res]"
as kmkaplan wrote (two and a half years ago, I just noticed!). The problem with your original commands:
res=' 'x # res = "\t\tx"
echo '['$res']' # expect [\t\tx]
isn't with echo; it's that the shell replaced the tab with a space before echo ever saw it.
echo is fine for simple output, like echo hello world, but you should use printf whenever you want to do something more complex. You can get echo to work, but the resulting code is likely to fail when you run it with a different echo implementation or a different shell.
You can also try:
echo Hello$'\t'world.
Put your string between double quotes:
echo "[$res]"
you need to use -e flag for echo then you can
echo -e "\t\t x"
From the bash man page:
Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
So you can do this:
echo $'hello\tworld'
Use the verbatim keystroke, ^V (CTRL+V, C-v, whatever).
When you type ^V into the terminal (or in most Unix editors), the following character is taken verbatim. You can use this to type a literal tab character inside a string you are echoing.
Something like the following works:
echo "^V<tab>" # CTRL+V, TAB
Bash docs (q.v., "quoted-insert")
quoted-insert (C-q, C-v)
Add the next character that you type to the line verbatim. This is how to insert key sequences like C-q, for example.
side note: according to this, ALT+TAB should do the same thing, but we've all bound that sequence to window switching so we can't use it
tab-insert (M-TAB)
Insert a tab character.
--
Note: you can use this strategy with all sorts of unusual characters. Like a carriage return:
echo "^V^M" # CTRL+V, CTRL+M
This is because carriage return is ASCII 13, and M is the 13th letter of the alphabet, so when you type ^M, you get the 13th ASCII character. You can see it in action using ls^M, at an empty prompt, which will insert a carriage return, causing the prompt to act just like you hit return. When these characters are normally interpreted, verbatim gets you get the literal character.
Using echo to print values of variables is a common Bash pitfall.
Reference link:
http://mywiki.wooledge.org/BashPitfalls#echo_.24foo
If you want to use echo "a\tb" in a script, you run the script as:
# sh -e myscript.sh
Alternatively, you can give to myscript.sh the execution permission, and then run the script.
# chmod +x myscript.sh
# ./myscript.sh
res="\t\tx"
echo -e "[${res}]"

Resources