【问题标题】:Searching a file (grep/awk) for 2 carriage return/line-feed characters在文件 (grep/awk) 中搜索 2 个回车符/换行符
【发布时间】:2015-09-24 22:11:43
【问题描述】:

我正在尝试编写一个脚本来简单地计算文件中 \r\n\r\n 的出现次数。 (以 vim 二进制模式打开示例文件会在正确的位置显示 ^M 字符,并且换行符仍被读取为换行符。

无论如何,我知道有很多解决方案,但它们似乎无法满足我的需求。

例如awk -e '/\r/,/\r/!d' 或使用 $'\n' 作为 grep 语句的一部分。

但是,这些似乎都不能产生我需要的东西。我找不到带有 grep 的“技巧”的\r\n\r\n 模式,因为它只是扩展了一个变量。 awk 解决方案是贪婪的,因此得到的行数比我想要/需要的多。

切换 grep 到 binary/Perl/no-newline 模式似乎更接近我想要的, 例如grep -UPzo '\x0D',但我真正想要的是grep -UPzo '\x0D\x00\x0D\x00',它不会产生我想要的输出。

这似乎是一个如此简单的任务。

【问题讨论】:

    标签: bash awk grep


    【解决方案1】:

    默认情况下,awk 将\n 视为记录分隔符。这使得计算\r\n\r\n 变得非常困难。如果我们选择其他一些记录分隔符,比如一个字母,那么我们可以很容易地统计这个组合的出现。因此:

    awk '{n+=gsub("\r\n\r\n", "")} END{print n}' RS='a' file
    

    这里,gsub 返回替换的次数。这些相加,在达到file 的末尾后,我们打印总数。

    示例

    在这里,我们使用 bash 的 $'...' 构造来显式添加换行符和换行符:

    $ echo -n $'\r\n\r\n\r\n\r\na' | awk '{n+=gsub("\r\n\r\n", "")} END{print n}' RS='a' 
    2
    

    替代解决方案 (GNU awk)

    我们可以告诉它把\r\n\r\n当作记录分隔符,然后返回记录数的计数(减1):

     cat file <(echo 1) | awk 'END{print NR-1;}' RS='\r\n\r\n'
    

    在 awk 中,RS 是记录分隔符,NR 是记录数的计数。由于我们使用的是多字符记录分隔符,因此这需要 GNU awk。

    如果文件以\r\n\r\n 结尾,则上述内容将减一。为避免这种情况,echo -n 1 语句用于确保文件中最后一个 \r\n\r\n 之后始终至少有一个字符。

    示例

    在这里,我们使用 bash 的 $'...' 构造来显式添加换行符和换行符:

    $ echo -n $'abc\r\n\r\n' | cat - <(echo 1) | awk 'END{print NR-1;}' RS='\r\n\r\n'
    1
    $ echo -n $'abc\r\n\r\ndef' | cat - <(echo 1) | awk 'END{print NR-1;}' RS='\r\n\r\n'
    1
    $ echo -n $'\r\n\r\n\r\n\r\n' | cat - <(echo 1) | awk 'END{print NR-1;}' RS='\r\n\r\n'
    2
    $ echo -n $'1\r\n\r\n2\r\n\r\n3' | cat - <(echo 1) | awk 'END{print NR-1;}' RS='\r\n\r\n'
    2
    

    【讨论】:

    • 并非所有awk 的实现都允许RS 的值使用任意正则表达式; GNU awk 可以,但在 POSIX awk 中,多字符 RS 会产生未定义的结果。
    • @chepner 好的。所以,第一个解决方案,带有单个字符RS,是可移植的。第二个不是。我更新了答案以说明第二个仅限于 GNU awk。感谢您的信息!
    猜你喜欢
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多