Control Characters – Why ^M and \r Behave Inconsistently

asciicontrol-charactershistoryterminalterminal-emulator

Perhaps there are already answers out there that indirectly answer my question, but I've read many of them and haven't yet found a satisfactory answer to this discrepancy.

The original meaning of carriage return comes from old teleprinters: it meant to move the print head to the left in the current line. If you kept writing in the current line, you would be overwriting what was already written. Nowadays, we can specify this behaviour with the text symbol \r, which is typed explicitly within a string. For example, in Python you can do print('hello\rgoodbye'), and in the terminal you can do echo $'hello\rgoodbye', and in both cases you will only see goodbye.

In contrast, a pseudo-carriage return can also be inserted interactively with the ASCII control character ^M (typed with Ctrl-M or with Enter). I call it pseudo-carriage return because even though it is widely called carriage return, surprisingly it doesn't insert \r, but rather it inserts \n, which is the symbol for a new line.

So interactively typing hello, then Ctrl-M, then goodbye, surprisingly doesn't achieve the equivalent of hello\rgoodbye, but rather the equivalent of hello\ngoodbye.

Isn't this very inconsistent? What is the rationale behind this?

Best Answer

This is the result of the terminal’s input handling: by default, the terminal device driver (software in the kernel of your operating system to handle input from the terminal, not the terminal itself) converts CtrlM to CtrlJ. You can see this by running

od -t x1

and then entering both characters:

$ od -t x1
^M
^J
^D
0000000 0a 0a
0000002

(You won’t see ^M etc. on screen.)

Thus when you type CtrlM, whatever program is processing your input doesn’t see a carriage return, it sees a line feed instead.

This can be configured with stty icrnl. See Understanding Return, Enter, and stty icrlf for a similar question.