【问题标题】:How to match CR CR LF newline pattern [duplicate]如何匹配 CR CR LF 换行符模式 [重复]
【发布时间】:2026-02-02 21:00:01
【问题描述】:

在 Windows 10 环境中,我必须检查目录中有多少 CSV 文件(分隔符为“;”)具有这种奇怪的换行符模式:CR CR LF(或 \r\r\n,如果您愿意)。 但是,我既不能用 grep 也不能用 awk 匹配 \r\r。在 awk 上,我还尝试将 RS 更改为 ;FS 一个未使用的字符(#),但显然 awk 匹配单个 CR,而不是 CR CR。因此,Windows 中的 awk 将 CR CR LF 视为 CR LF 和 FNR 输出的记录数等于任何其他“正常结束行”文件。 奇怪的是,使用 Notepad++ 我可以清楚地看到 CR CR LF(导致额外的换行符,例如在 Excel 中),并且使用内置的正则表达式查找器,搜索 \r\r\n 匹配所有行。是否可以强制 awk 在不删除某些 CR 的情况下对原始文本文件执行操作?
该文件是这样的(我已经简化了一点):5 行,4 个 x 字段,由 ; 分隔,每行末尾有一个 CRCRLF。用 Notepad++(和 Excel)打开我看到 10 行。

我希望下面的 GNU awk 脚本会返回 16 5

BEGIN {RS = ";";FS = "#"; linecount = 0}
/\r\r/ {linecount = linecount + 1}
END {print FNR, linecount}

但是,它返回16 0。如果我搜索以匹配 /\r/,则会获得 16 5

所以基本上我担心 Windows CMD shell 在将流传递给 gawk 之前会剥离两个连续 CR 之一(或者更好地说,是用 LF 替换 CR LF 对),我想知道它是否是可以避免这种情况,因为我想使用 gawk 来检测有多少文件有这个奇怪的 CR CR LF 换行符。

我相信这里已经发布了一个非常相似的问题: In Perl, how to match two consecutive Carriage Returns?

【问题讨论】:

  • 不,我的问题是 CR CR LF 模式,我没有使用 CYGWIN。我已经通过 GOW 包安装了 gawk。然而,显然唯一的解决方案是要知道在 CR CR LF 上,在将数据流传递给 gawk 之前,一个 CR LF 令牌被 Windows 的 LF 替换,然后如果一些“单个”CR 仍然存在,这意味着这是一个“奇怪" 换行文件类型。我只是希望 gawk 能够像 Notepad++ 显然能够做到的那样解析原始 .txt 文件。

标签: regex awk grep newline


【解决方案1】:

在意识到有重复之后(感谢@tripleee):

在 MS-Windows 下,gawk(和许多其他文本程序)在输入时静默地将行尾 \r\n 转换为 \n,在输出时将 \n 转换为 \r\n。一个特殊的BINMODE 变量(c.e.)允许控制这些翻译,并解释如下:

  • 如果BINMODE 为“r”或一,则在读取时设置二进制模式(即,读取时不进行翻译)。
  • 如果BINMODE 为“w”或二,则在写入时设置二进制模式(即,写入时不进行翻译)。
  • 如果BINMODE 为“rw”或“wr”或三个,则为读写设置二进制模式。
  • BINMODE=non-null-stringBINMODE=3 相同(即,在读取或写入时没有翻译)。但是,如果字符串不是“rw”或“wr”之一,gawk 会发出警告消息。

来源:https://www.gnu.org/software/gawk/manual/gawk.html#PC-Using

要保持 awk 的原始 POSIX 样式,您应该使用 BINMODE=3。使用 awk(或任何未修改的版本),您应该可以通过检查记录是否以 \r\r 结尾来轻松完成此操作。这是因为 awk 默认 0 使用 RS="\n" 在记录中拆分文件。由于 GOW 使用的是 GNU awk,因此您有以下选择:

计数文件:

awk '/\r\r$/{f++; nextfile} END {print f,"files match"}' BINMODE=3 *.csv

统计文件并打印文件名:

awk '/\r\r$/{f++; print FILENAME; nextfile} END {print f,"files match"}' BINMODE=3 *.csv

计数文件,打印文件名和行数:

awk '(FNR==1){if (c) {print fname, c; f++}; c=0; fname=FILENAME}
     /\r\r$/{c++}
     END { print f,"files match" }' BINMODE=3 *.csv

注意:在任何 POSIX 系统上删除 BINMODE=3

【讨论】:

  • 是的,这是一种更好、更有效的方法。
【解决方案2】:

你可以试试 GNU grep 的 -z-P 开关,试试这个:

grep -zcP "\r\r\n" *.csv | awk -F: "$2{c++}END{print c}"

所以我像你说的那样创建了一个文件:

awk 'BEGIN{ORS="\r\r\n"; OFS=";"; for(i=1;i<11;i++)print "aa","bb","cc",i>"strange.csv"}'

我可以像这样在 csv 文件中搜索\r\r\n

> grep -zcP "\r\r\n" *.csv
file1.csv:0
file2.csv:0
file3.csv:0
file_a.csv:0
file_b.csv:0
results.csv:0
strange.csv:1

并结合awk:

awk -F: "$2{c++}END{print c}"

获取计数:

> grep -zcP "\r\r\n" *.csv | awk -F: "$2{c++}END{print c}"
1

或者,只使用awk

> awk 'BEGIN{RS="";}/\r\r\n/{c++;nexfile}END{print c}' *.csv
1                                                    

因此,以上grepawk 示例,读取整个文件而不是每轮处理每一行。

【讨论】:

  • 谢谢 Tiw,你的 grep 建议是我正在寻找的,不幸的是我得到了这个回应 grep: -P 和 -z 选项不能组合。我在 Win10 的 CMD shell 下使用 GNU grep 2.5.4。
  • @atcher 那最好升级一下 :) fyi,我用的是 GNU grep 3.0