如何将Windows行末格式转换为Unix行末格式(CR / LF到LF)

101

我是一名Java开发人员,正在使用Ubuntu进行开发。该项目是在Windows上使用Eclipse创建的,并且使用了Windows-1252编码。

为了转换为UTF-8编码,我使用了recode程序:

find Web -iname \*.java | xargs recode CP1252...UTF-8

运行此命令会出现以下错误:

recode: Web/src/br/cits/projeto/geral/presentation/GravacaoMessageHelper.java failed: Ambiguous output in step `CR-LF..data

我已经搜索过了,在Bash和Windows,Recode:步骤`data..CR-LF'中的歧义输出中找到了解决方案:

将换行符从CR/LF转换为单个LF:使用Vim编辑文件,执行命令:set ff=unix并保存文件。现在,Recode应该可以无错误地运行。

很好,但我有很多文件需要删除CR/LF字符,并且我不能打开每个文件来执行此操作。Vi不提供任何用于Bash操作的命令行选项。

能否使用sed来完成此操作?如何实现?


当尝试重新编码一个包含混合 DOS (\r\n - CRLF) 和 Unix (\n LF) 换行符的文件时,recode 会产生这个错误。不幸的是,曾经是二进制文件的 fromdos 目前是 recode 的别名,也存在这个问题。 - Tomas
你不能执行 vim +ex_command_one +ex_command_two ... file 吗? - derekdreery
1
惊人!答案中没有 awk 的解决方案。 - Gerold Broser
9个回答

139

应该有一个名为 dos2unix 的程序,可以为您修复行尾。如果它还没有安装在您的Linux系统上,可通过软件包管理器获取。


2
我已经安装了tofrodos,提供了fromdos命令,但问题仍然存在。执行fromdos -a GravacaoMessageHelper.java; recode CP1252...UTF-8 GravacaoMessageHelper.java命令后返回以下信息:recode: GravacaoMessageHelper.java失败:在步骤“CR-LF..data”中输出不明确。 - MaikoID
1
@MaikoID:那你有更大的问题了。recode 不应该关心行尾,因为 CR 只是另一个要转换的字符。而且在我的机器上它似乎并不关心。 - cHao
1
fromdos 只是 recode 的别名,对于混合了 dos (\r\n - CRLF) 和 unix (\n LF) 编码的文件,这将产生 OP 提到的错误。只有 dos2unix 能够普遍地工作。 - Tomas
1
dos2unix 可以通过 homebrew 在 OS X 上安装: "brew install dos2unix" - Joseph Sheedy
1
仅作跟进,我遇到了同样的问题,最终使用了以下命令:find ./ -name "*.java" -exec dos2unix {} + - amracel
在Ubuntu MATE 20.04(Focal Fossa)中,默认情况下未安装dos2unix。 - Peter Mortensen

107

sed无法匹配\n,因为在将行放入模式空间之前,尾随换行符会被删除,但它可以匹配\r,所以您可以通过删除\r将\r\n(DOS)转换为\n(Unix):

sed -i 's/\r//g' file

警告:这将更改原始文件。

但是,您不能通过此方法从Unix EOL更改为DOS或旧Mac(\r)。更多阅读资料请参见:如何使用sed替换换行符(\n)?


4
这是一个不错的解决方案!但你需要注意 sed -i 会修改原始文件!因为人们没有期望 sed 行为如此,所以在这里适当警告是必要的。许多人不知道 -i,所以他们会尝试 sed -i ... file > file2,并且不会预期原文件被修改。 - Tomas
并非所有的 sed 变体都能识别非标准符号序列 \r。在这种情况下,请尝试使用字面上的 ctrl-M 字符(在许多 shell 中,键入 ctrl-V ctrl-M 以生成字面控制字符)。 - tripleee
对我来说是个不错的解决方案,它可以在我的 .ksh 文件上运行。 - user3437460
这个在 Linux 文件上使用也安全吗?所以,如果您不确定,可以直接运行而无需事先检查吗? - Natan

17

实际上,Vim允许你所要找的功能。进入Vim,然后输入以下命令:

:args **/*.java
:argdo set ff=unix | update | next

这两个命令中的第一个将参数列表设置为与**/*.java匹配的所有Java文件(递归搜索)。这些命令中的第二个对参数列表中的每个文件进行以下操作:

  • 将行尾设置为Unix样式(您已经知道这个)
  • 当且仅当文件发生更改时,才将文件写出
  • 继续到下一个文件

这可能比在for循环中使用dos2unix慢得多,但是知道如何在Vim中完成它仍然很好! - jpaugh
2
我 ::heart:: 我的vim。谢谢你。 - Jono

12

我对jichao的答案稍有异议。你实际上可以相当容易地完成他所讲的一切。只需要查找行尾处的回车符号,而不是寻找\n

sed -i 's/\r$//' "${FILE_NAME}"

要从Unix切换回DOS,只需查找行中的最后一个字符并向其添加一个进纸符。(我会添加-r以便在grep正则表达式中更轻松地使用。)

sed -ri 's/(.)$/\1\r/' "${FILE_NAME}"

理论上,可以通过在最后一个示例中添加代码将文件改为Mac风格,该代码还将下一行输入附加到第一行,直到处理完所有行。不过,我不会在这里尝试制作那个示例。

警告:-i会更改实际文件。如果您想要备份,请在-i之后添加一串字符。这将把现有文件移动到以您添加的字符结尾的同名文件。

更新:Unix转DOS的转换可以简化并更高效,无需寻找最后一个字符。这也使我们不需要使用-r即可运行。

sed -i 's/$/\r/' "${FILE_NAME}"

2
我喜欢你的建议,但它只是缺少一个闭单引号。它应该是:sed -ri 's/(.)$/\1\r/' ${FILE_NAME} - mgouin
2
@mgouin 感谢您的指出。我已经加上了缺失的单引号。 - John Chesshir
1
将LF转换为CRLF时,不需要捕获行末之前的一些最后字符,这可能会影响性能。在我的情况下,只需执行sed -i 's/$/\r/' ${FILE_NAME}即可。 - Thomas Urban
“-r”选项不是可移植的;如果你的“sed”没有它,也许可以尝试使用“-E”。 - tripleee
@ThomasUrban 谢谢你提供的信息。我已经更新了简化版本,以便让人们更快地看到它。但是,我仍然保留原始表达式,这样阅读你的评论的人不会被混淆。 - John Chesshir

9
命令也可以实现这个功能:
tr -d '\15\32' < winfile.txt > unixfile.txt

并且应该对您可用。

您需要在脚本内部运行tr,因为它无法使用文件名。例如,创建一个名为myscript.sh的文件:

#!/bin/bash

for f in `find -iname \*.java`; do
    echo "$f"
    tr -d '\15\32' < "$f" > "$f.tr"
    mv "$f.tr" "$f"
    recode CP1252...UTF-8 "$f"
done

运行 myscript.sh 将处理当前目录及其子目录中的所有 java 文件。

我该如何适应查找Web -iname *.java | xargs recode CP1252...UTF-8的命令? - MaikoID
你需要在bash脚本中运行tr,因为它不能处理文件名。我会在我的答案中编辑一个示例脚本。 - KeithL
谢谢您的回答,但错误仍然存在 =| 在“CR-LF..data”步骤中输出模糊。 - MaikoID

6

为了克服这个问题,我们需要利用IT技术。

Ambiguous output in step `CR-LF..data'

简单的解决方案是添加-f标志,以强制进行转换。

这里是答案。 - caduceus

1
使用下面的命令,使用sed将文件的行结束符转换为Unix格式:
sed -i 's/\r$//' file_name.sh

这个命令将会将所有行末的回车符(CR)替换为空。

0

尝试使用这里找到的Bryan Maupin的Python脚本(我稍微修改了一下,使其更通用):

#!/usr/bin/env python

import sys

input_file_name = sys.argv[1]
output_file_name = sys.argv[2]

input_file = open(input_file_name)
output_file = open(output_file_name, 'w')

line_number = 0

for input_line in input_file:
    line_number += 1
    try:  # first try to decode it using cp1252 (Windows, Western Europe)
        output_line = input_line.decode('cp1252').encode('utf8')
    except UnicodeDecodeError, error:  # if there's an error
        sys.stderr.write('ERROR (line %s):\t%s\n' % (line_number, error))  # write to stderr
        try:  # then if that fails, try to decode using latin1 (ISO 8859-1)
            output_line = input_line.decode('latin1').encode('utf8')
        except UnicodeDecodeError, error:  # if there's an error
            sys.stderr.write('ERROR (line %s):\t%s\n' % (line_number, error))  # write to stderr
            sys.exit(1)  # and just keep going
    output_file.write(output_line)

input_file.close()
output_file.close()

你可以使用那个脚本

$ ./cp1252_utf8.py file_cp1252.sql file_utf8.sql

-1

返回Windows,告诉Eclipse将编码更改为UTF-8,然后返回Unix并在文件上运行d2u


1
虽然如果有很多文件,这可能比你愿意付出的努力更多... - Jonathan
d2u是什么,它在哪里可以找到? - Jesper Rønn-Jensen
它会不时地更名。看起来Ubuntu在10.04中称其为fromdos,并且它是tofrodos软件包的一部分。 - Jonathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接