Macos – Can Terminal.app be made to respect ANSI escape codes

ansi-escapeiterm2macosterminal

I notice that with the TERM environment variable set to either xterm or xterm-256color that Mac OS X’s Terminal.app utility respects most ANSI escape codes, as least when those escape codes pertain to changing text color.

For example:

echo -e "\033[0;31mERROR:\033[0m It worked"

Produces:

Screenshot

However, I’m more interested in the cursor location manipulation functionality afforded by ANSI escape codes. Unfortunately, that type of code doesn’t seem to work too well in Terminal.app, from what I’ve been able to gather. For example, what I want to do is something like this:

echo -e "\033[sHello world\033[uG'day"

ESC[s Saves the current cursor position, whereas ESC[u restores the last saved position. As a result of running the script above, I’d expect the five characters in “G'day” to overwrite the five characters of “Hello” after the cursor has been repositioned, producing the following:

G'day world

Indeed, this is exactly what I get with iTerm2.app, ConEmu for Windows (running either MinGW or MSYS Git’s copy of bash.exe), etc. What I see in Terminal.app, however, is this:

Hello worldG'day

Is there a reason for this, aside form Terminal.app simply lacking support for these codes? Is there a way to enable this functionality? Is there a chance I have something misconfigured? My TERM setting? Something else?

I’ve been searching everywhere, but haven’t found anything relevant to Terminal.app, specifically. I find it odd that it would support colored text via ANSI escape codes, but not cursor repositioning through the exact same technology. That seems like a fairly arbitrary subset of a fairly well-defined standard. That’s what makes me think I’ve got something misconfigured, rather than that Terminal.app is the one to blame… but, I suppose it’s possible that it simply can’t be done. (Probably one of the reasons iTerm2 exists in the first place?)

If anyone could shed some light on this situation, it’d be much appreciated!

UPDATE

So, I've done a bit more reading and experimenting, and discovered the following oddity:

After looking into n.m.'s answer below, I decided to write out the bytes that were being returned by tput to a file to see how they differed from the regular ANSI instruction.

$ echo "$(tput sc)Hello world$(tput rc)G'day" > out.bin
$ cat -e out.bin
^[7Hello world^[8G'day$

It appears that everything works as expected if I send it the sequences ESC 7 and ESC 8, but NOT if I send it ESC [s and ESC [u, which as I understand things is the more typical representation of the ANSI SCP and RCP codes (Save Cursor Position and Restore Cursor Position, respectively). Since putting ASCII decimal characters 7 or 8 next to an escaped octal byte representation is impossible (\0337 != ESC), environment variables can be used instead to avoid relying on tput:

$ esc=$'\033'
$ csi="${esc}["
$ echo "${csi}0;31mERROR:${csi}0m It worked."
ERROR: It worked.  # Color works, as before
$ echo "${csi}sHello world${csi}uG'day"
Hello worldG'day   # No dice
$ echo "${esc}7Hello world${esc}8G'day"
G'day world        # Success

I'm not sure why this is. If ESC 7 and ESC 8 are some kind of proprietary or custom code for ANSI SCP and RCP that has the potential to vary from terminal implementation to terminal implementation, it would explain to me why tput was created in the first place.

Unfortunately, I am unable to use tput for what I am currently working on, as I am not working exclusively in a bash environment. I am more curious as to how the raw bytes are interpreted from terminal to terminal, and more specifically, whether or not there's a way to get Terminal.app to respect the same ANSI escape codes that all of the other terminal emulators I've tried seem to have no problem with. Is that possible? At this point, I'm beginning to think it simply might not be, which is fine, but it would be nice to know for sure, and possibly also to learn the reason why.

Best Answer

Don't use ANSI codes. Use proper terminfo-based techniques. Xterm-based terminals are not specified to support all ANSI codes. Some do for compatibility, some don't.

Save cursor position sequence is given by the tput sc command and the restore cursor position is tput rc.

echo -e "$(tput sc)Hello world$(tput rc)G'day"

should work on any terminal that supports these sequences.

To see a readable representation of the supported sequences, use the infocmp command. The output might be quite long. If you are interested in sc and rc:

infocmp | grep --color ' [sr]c='

Disclaimer: tested on my Linux machine, don't have a Mac nearby.

UPDATE

Terminal.app is modeled after xterm and xterm is modelled after the VT100 terminal. VT100 did not implement CSI u and CSI s sequences, but used DEC private ESC 7 and ESC 8 sequences (source). Later VT models supported both CSI s/u and ESC 7/8, under different name and with slightly different functionality (source).

ECMA 48 doesn't seem to specify any save/restore cursor position sequences (source (PDF)), or I could not find them there. I don't know where CSU s/u come from. Their name in VT510 documentation suggests they are somehow connected with SCO. This source suggests they are in fact private sequences with no standard meaning. SUN terminals use SCI s to do a reset. It is probably an error to brand these two sequences ANSI.

Modern versions of xterm and other X11 terminal programs (konsole, rxvt...) do support both ESC 7/8 and CSI s/u, but the terminfo database only advertises ESC 7/8. Terminal.app apparently only supports ESC 7/8.

Related Topic