如何在不修改文件的情况下测试文件是使用 CRLF 还是 LF?

Ada*_*ski 77 bash text-processing newlines

我需要定期运行一个命令,以确保某些文本文件保持在 Linux 模式下。不幸的是dos2unix总是修改文件,这会弄乱文件和文件夹的时间戳并导致不必要的写入。

我编写的脚本是用 Bash 编写的,所以我更喜欢基于 Bash 的答案。

Sam*_*ard 52

您可以dos2unix用作过滤器并将其输出与原始文件进行比较:

dos2unix < myfile.txt | cmp - myfile.txt
Run Code Online (Sandbox Code Playgroud)

  • 非常聪明和有用,因为它测试**完整的文件**,而不仅仅是第一行或几行。 (3认同)

Gyp*_*aut 47

cat -A

$ cat file
hello
hello
Run Code Online (Sandbox Code Playgroud)

现在,如果这个文件是在 *NIX 系统中制作的,它将显示

$ cat -A file
hello$
hello$
Run Code Online (Sandbox Code Playgroud)

但是如果这个文件是在 Windows 中制作的,它会显示

$ cat -A file
hello^M$
hello
Run Code Online (Sandbox Code Playgroud)

^M代表CR$代表LF。请注意,Windows 没有保存最后一行CRLF

这也不会更改文件内容。

注意:这不应该在自动化脚本中使用,只是为了快速检查。对于自动化脚本,我建议您查看@Samuel Edwin Ward 的答案

  • +1 到目前为止最好的答案。没有依赖关系,没有复杂的 bash 脚本。只是`-A` 猫。一个提示是使用`cat -A file | 如果文件太大,则更少。我敢肯定,对于一个特别长的文件,必须检查文件结尾的情况并不少见。(按`q`少离开) (3认同)
  • 谢谢,@Oskar,确实 `cat -A` 在 macOS 上不起作用,但 `cat -ve` 没问题。 (3认同)
  • 最好和最简单的解决方案!需要更多的投票。 (2认同)
  • 我认为`cat -ve` 也可以使用并且更便携。 (2认同)

j88*_*376 27

如果目标只是为了避免影响时间戳,dos2unix则使用-kor--keepdate选项可以保持时间戳不变。它仍然需要写入以制作临时文件并重命名它,但您的时间戳不会受到影响。

如果对文件的任何修改是不可接受的,您可以使用此答案中的以下解决方案。

find . -not -type d -exec file "{}" ";" | grep CRLF
Run Code Online (Sandbox Code Playgroud)

  • 你的意思是 grep 也可以这样接受 CR 和 LF 吗? (7认同)
  • @bodacydo http://stackoverflow.com/questions/73833/how-do-you-search-for-files- contains-dos-line-endings-crlf-with-grep-on-linu/73969#73969 说`查找... -exec 文件... | 带有 DOS 行结尾(即字节 0D 0A)的文件的 grep CRLF` 将得到类似以下内容:`./1/dos1.txt: ASCII 文本,带有 CRLF 行终止符` 如您所见,这包含实际的字符串 CRLF因此与寻找简单字符串 CRLF 的 `grep` 匹配。 (2认同)

don*_*sti 24

您可以尝试使用grep八进制的 CRLF 代码:

grep -U $'\015' myfile.txt
Run Code Online (Sandbox Code Playgroud)

或十六进制:

grep -U $'\x0D' myfile.txt
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这种 `grep` 用法,因为它允许我使用 `grep -lU $'\x0D' *` 轻松列出目录中的所有此类文件,并将输出传递给 `xargs`。 (2认同)
  • 这仅测试 CR,不一定是 CR-LF 序列。 (2认同)

小智 23

由于版本7.1dos2unix 有一个-i,--info选项来获取有关换行符的信息。您可以使用 dos2unix 本身来测试哪些文件需要转换。

例子:

dos2unix -ic *.txt | xargs dos2unix
Run Code Online (Sandbox Code Playgroud)


Ber*_*rtS 17

第一种方法(grep):

计算包含回车的行数:

[[ $(grep -c $'\r' myfile.txt) -gt 0 ]] && echo dos
Run Code Online (Sandbox Code Playgroud)

计算以回车符结尾的行数:

[[ $(grep -c $'\r$' myfile.txt) -gt 0 ]] && echo dos
Run Code Online (Sandbox Code Playgroud)

这些通常是等效的;在一行的内部(即不在末尾)回车是很少见的。

更高效:

grep -q $'\r' myfile.txt && echo dos
Run Code Online (Sandbox Code Playgroud)

这样效率更高

  1. 因为它不需要将计数转换为 ASCII 字符串,然后将该字符串转换回整数,并将其与零进行比较,并且
  2. 因为grep -c需要读取整个文件,计算模式的所有出现次数,同时grep -q可以在看到模式第一次出现时退出。

笔记:

  • 综上所述,您可能需要添加-U选项(即使用-cU-qU),因为 GNU 会grep猜测该文件是否为文本文件。如果它认为文件是文本,它会忽略行尾的回车符,试图使$正则表达式“正确”工作——即使正则表达式是\r$! 指定-U(或--binary) 否决了这种猜测,导致grep将文件视为二进制文件并将数据逐字传递给匹配机制,CR 结尾完好无损。
  • 不要这样做grep … $'\r\n' myfile.txt,因为grep将其\n视为模式分隔符。就像grep -E 'foo|'查找包含foo或 空字符串的行一样, grep $'\r\n'查找包含\r或 空字符串的行,并且每一行都匹配一个空字符串。

第二种方法(file):

[[ $(file myfile.txt) =~ CRLF ]] && echo dos
Run Code Online (Sandbox Code Playgroud)

因为file报告如下:

myfile.txt: UTF-8 Unicode text, with CRLF line terminators
Run Code Online (Sandbox Code Playgroud)

更安全的变体:

[[ $(file -b - < myfile.txt) =~ CRLF ]] && echo dos
Run Code Online (Sandbox Code Playgroud)

在哪里

请注意,检查来自 的输出file 可能不适用于非英语语言环境。


gle*_*man 6

为您提供 bash 功能:

# return 0 (true) if first line ends in CR
isDosFile() {
    [[ $(head -1 "$1") == *$'\r' ]]  
}
Run Code Online (Sandbox Code Playgroud)

然后你可以做类似的事情

streamFile () {
    if isDosFile /tmp/foo.txt; then
        sed 's/\r$//' "$1"
    else
        cat "$1"
    fi
}

streamFile /tmp/foo.txt | process_lines_without_CR
Run Code Online (Sandbox Code Playgroud)

  • 您不必在示例中使用 `isDosFile()`: `streamFile() { sed 's/\r$//' "$1" ; }`。 (3认同)

小智 6

使用file

$ file README.md
README.md: ASCII text, with CRLF line terminators

$ dos2unix README.md
dos2unix: converting file README.md to Unix format...

$ file README.md
README.md: ASCII text
Run Code Online (Sandbox Code Playgroud)


Kei*_*son 5

如果文件具有 DOS/Windows 样式的 CR-LF 行结尾,那么如果您使用基于 Unix 的工具查看它,您将在每行末尾看到 CR ('\r') 字符。

这个命令:

grep -l '^M$' filename
Run Code Online (Sandbox Code Playgroud)

如果文件包含一行或多行带有 Windows 样式行结尾的行,则将打印filename,否则将不打印任何内容。只不过^M必须是文字回车符,通常通过键入Ctrl+V后跟Enter (或Ctrl+V然后Ctrl+ M)在终端中输入。bash shell 允许您编写文字回车符$'\r'此处记录),因此您可以编写:

grep -l $'\r$' filename
Run Code Online (Sandbox Code Playgroud)

其他外壳可能提供类似的功能。

您可以使用其他工具代替:

awk '/\r$/ { exit(1) }' filename
Run Code Online (Sandbox Code Playgroud)

如果文件包含任何 Windows 样式的行结尾,则这将以状态退出1(设置$?为),如果不包含,1则状态为,使其在 shell语句中有用(注意缺少括号):0if[]

if awk '/\r$/ { exit(1) }' filename ; then
    echo filename has Unix-style line endings
else
    echo filename has at least one Windows-style line ending
fi
Run Code Online (Sandbox Code Playgroud)

文件可以包含 Unix 样式和 Windows 样式的混合行结尾。我在这里假设您想要检测具有任何Windows 样式行结尾的文件。