为什么在查看某些文件时终端输出中会出现 `^M`?

Why does `^M` appear in terminal output when looking at some files?

我正在尝试使用 curl 将文件发送到端点并将文件保存到计算机。

从 Linux 发送 curl 并将其保存在机器上效果很好, 但是从 Windows 做同样的卷曲是在行的每一端添加 ^M 字符。

我正在打印文件,然后再保存它,但看不到 ^M。保存后仅在远程计算机上查看文件显示 ^M.

简单的字符串替换似乎不起作用。

为什么要添加 ^M?我该如何防止这种情况?

快速回答:那是 carriage return. They're a harmless but mildly irritating artifact of how Windows encodes text files. You can strip them out of your files with dos2unix。您可以将大多数文本编辑器配置为使用 "Unix Line Endings" 或 "LF Line Endings",以防止它们出现在您将来从 Windows 台 PC 创建的新文件中。


长答案(有一些历史琐事):

在纯文本文件中,当您创建新行(通过按 enter/return)时,文件中会嵌入一个 "line break"。在 Unix/Linux 上,这是单个字符,'\n',即 "line feed"。在 Windows 上,这是两个连续字符,'\r\n',"carriage return" 后跟 "line feed"。

当物理 teletype terminals(其行为很像打字机)仍在使用时,"line feed" 字符表示 "move the paper up to the next line","carriage return" 字符表示 "slide the carriage all the way over so the typing head is on the far left".从一开始,几乎所有的电传打字机终端都支持隐式回车return;即,触发换行会自动触发回车 return。从事后来演变成 Windows 的开发人员决定,最好包括显式回车 return,以防万一(出于某种原因)电传打字机不隐式执行。另一方面,Unix 开发人员选择使用隐式传输的假设 return.

回车 return 和换行 ASCII Control Characters 这意味着它们没有作为独立可打印字符的可见表示,而是影响输出光标本身(在这种情况下,位置输出光标)。

您看到的“^M”是回车 return 字符的替代表示,由未完全 "cook" 其输出的程序使用(即不应用某些 ASCII 控制字符的影响)。 (其他控制字符有其他以“^”开头的表示,“^”字符在某些Unix程序中也用于表示"ctrl"键盘键,如nano。)

您可以使用 dos2unix 将行结尾从 Windows 风格转换为 Unix 风格。

$ curl https://example.com/file_with_crlf.txt | dos2unix > file.txt

在某些发行版中,默认包含此工具,在其他发行版中,可以通过包管理器安装它(例如,在 Ubuntu、sudo apt install dos2unix 上)。还有一个程序包,unix2dos,用于反函数。

大多数 "smart" 用于编码的文本编辑器(Sublime、Atom、VS Code、Notepad++ 等)将愉快地使用 Windows 风格或 Unix 风格的行结尾(这可能需要更改一些配置选项)。通常,行尾是通过扫描文件内容自动检测的,通常新文件是使用操作系统的本机行尾(默认情况下)创建的。甚至 Notepad 的新版本也支持 Unix 风格的行结尾。另一方面,某些 Unix 工具会在出现 Windows 样式的换行符时产生奇怪的结果。如果你的代码库将被 Unix 和 Windows 操作系统上的人们使用,那么最好的办法是在所有地方使用 Unix 风格的行尾。

Windows 上的

Git 也有一个可选模式,可以使用 Windows 风格的换行符检出所有文件,但使用 Unix 风格的换行符将它们重新检入。


旁注(有趣,但与您的问题没有直接关系):

回车 return 的实际作用(在现代虚拟终端上,无论是 Windows 还是 Unix)是将输出光标移动到行首。如果您使用没有换行的回车 return,您可以 "overwrite" 已经打印的字符串的一部分。

$ printf "dogdog" ; printf "\rcat\n"
catdog

一些 Unix 程序使用它来异步更新最后一行输出的一部分,以实现实时更新进度指示器之类的东西。例如,curl,如果文件内容通过管道传输到其他地方,它会在 stdout 上显示下载进度。

另外:如果你有一个工具可以尽可能按字面解释 Windows 风格的行结尾,并且你给它一个带有 Unix 风格的行结尾的字符串,比如 "hello\nworld",你会得到这样的输出:

hello
     world

幸运的是,这样的实现极为罕见,一般来说,绝大多数 Windows 工具都可以毫无问题地呈现与 Windows 风格的行结束相同的 Unix 风格的行结束。