【问题标题】:How to convert Windows end of line in Unix end of line (CR/LF to LF)如何在 Unix 行尾转换 Windows 行尾(CR/LF 到 LF)
【发布时间】:2011-04-22 21:48:15
【问题描述】:

我是一名 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 and Windows, Recode: Ambiguous output in step `data..CR-LF' 中找到了解决方案,它说:

将行尾从 CR/LF 转换为 单个 LF:用 Vim 编辑文件, 发出命令:set ff=unix 并保存 文件。现在重新编码应该运行 没有错误。

很好,但是我有很多文件要从中删除 CR/LF 字符,而且我无法打开每个文件来执行此操作。 Vi 没有为 Bash 操作提供任何命令行选项。

可以使用 sed 来执行此操作吗?怎么样?

【问题讨论】:

  • recode 在尝试使用混合 dos (\r\n - CRLF) 和 unix (\n LF) 换行编码重新编码文件时产生此错误。不幸的是fromdos,以前是二进制文件,现在是 recode 的别名,它有这个问题。
  • 你不能吗vim +ex_command_one +ex_command_two ... file
  • 令人惊讶!答案中没有awk 解决方案。

标签: windows linux end-of-line


【解决方案1】:

jichao's answer 有一点例外。你实际上可以很容易地完成他刚才所说的一切。无需寻找\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}"

【讨论】:

  • 我喜欢你的建议,但它只是缺少一个结束单引号。应该是: sed -ri 's/(.)$/\1\r/' ${FILE_NAME}
  • @mgouin 感谢您的注意。我添加了缺少的单引号。
  • 为了将 LF 转换为 CRLF,不需要捕获行尾前的最后一个字符,这也可能会影响性能。就我而言,这样做就足够了 sed -i 's/$/\r/' ${FILE_NAME} ...
  • -r 选项不可移植;如果您的sed 没有,不妨试试-E
  • @ThomasUrban 谢谢你的信息。我添加了一个简化的更新,以便人们更快地看到它。不过,我将保留原来的表达方式,以便阅读您评论的人不会对您的陈述感到困惑。
【解决方案2】:

实际上,Vim 确实允许您查找所需的内容。进入 Vim,然后输入以下命令:

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

这些命令中的第一个命令将参数列表设置为与**/*.java 匹配的每个文件,即所有Java 文件,递归。这些命令中的第二个依次对参数列表中的每个文件执行以下操作:

  • 将行尾设置为 Unix 样式(你已经知道了)
  • 写出文件iff它已被更改
  • 继续下一个文件

【讨论】:

  • 这可能比在 for 循环中使用 dos2unix 慢得多,但知道如何在 Vim 中做到这一点仍然很好!
  • 我 ::heart:: 我的 vim。谢谢你。
【解决方案3】:

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

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

警告:这将改变原始文件

但是,您不能由此从 Unix EOL 更改为 DOS 或旧 Mac (\r)。更多阅读:

How can I replace a newline (\n) using sed?

【讨论】:

  • +1 这是一个不错的解决方案!但是您应该注意,sed -i 会更改原始文件!因为人们不希望sed 表现得如此,所以警告在这里是合适的。知道-i的人不多,所以他们会尝试sed -i ... file > file2,不要指望原始文件会被修改。
  • 并非所有sed 变体都能识别非标准符号序列\r。在这种情况下尝试使用文字 ctrl-M 字符(在许多 shell 中,键入 ctrl-V ctrl-M 以生成文字控制字符)。
  • 对我来说很好的解决方案,它适用于我的 .ksh 文件。
【解决方案4】:

为了克服

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

简单的解决方案可能是添加-f 标志来强制转换。

【讨论】:

    【解决方案5】:

    试试Python script by Bryan Maupin found here(我稍微修改了一下使其更通用):

    #!/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
    

    【讨论】:

      【解决方案6】:

      tr 命令也可以这样做:

      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 重新编码 CP1252...UTF-8
      • 您需要在 bash 脚本中运行 tr,因为它不能用于文件名。我将使用示例脚本编辑我的答案。
      • 感谢答案,但错误仍然存​​在 =|步骤“CR-LF..data”中的模棱两可的输出
      【解决方案7】:

      应该有一个名为dos2unix 的程序会为您修复行尾。如果它还没有在你的 Linux 机器上,它应该可以通过包管理器获得。

      【讨论】:

      • 我安装了提供 fromdos 命令的 tofrodos,但问题仍然存在。 fromdos -a GravacaoMessageHelper.java;重新编码 CP1252...UTF-8 GravacaoMessageHelper.java 返回:重新编码:GravacaoMessageHelper.java 失败:步骤“CR-LF..data”中的输出不明确
      • @MaikoID:那你有更大的问题。无论如何,重新编码不应该关心行尾,因为 CR 只是另一个要转换的字符。而且它似乎不在我的机器上。
      • fromdos 只是recode 的别名,这将产生混合 dos (\r\n - CRLF) 和 unix (\n LF) 编码的文件中提到的错误 OP。只有dos2unix 通用。
      • dos2unix 在 OS X 上可以通过 homebrew 获得:“brew install dos2unix”
      • 为了跟进这个问题,我遇到了同样的问题并最终使用了以下内容:find ./ -name "*.java" -exec dos2unix {} +
      【解决方案8】:

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

      【讨论】:

      • 虽然文件很多,这可能比你愿意投入的工作要多...
      • 什么是 d2u,在哪里可以找到它?
      • 它偶尔会被重命名。看起来 Ubuntu 在 10.04 中将其称为 fromdos,它是软件包 tofrodos 的一部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-14
      • 2010-09-06
      • 2010-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多