如何找到不以换行符/换行符结尾的文件?

时间:2011-01-07 23:01:19

标签: linux perl bash

如何列出不以换行符结尾的普通文本(.txt)文件名?

例如:列出(输出)此文件名:

$ cat a.txt
asdfasdlsad4randomcharsf
asdfasdfaasdf43randomcharssdf
$ 

并且不列出(输出)此文件名:

$ cat b.txt
asdfasdlsad4randomcharsf
asdfasdfaasdf43randomcharssdf

$

13 个答案:

答案 0 :(得分:23)

使用pcregrep,一个perp兼容的正则表达式版本的grep,它支持使用-M标志的多行模式,如果最后一行有换行符,可用于匹配(或不匹配):

pcregrep -LMr '\n$' .

在上面的示例中,我们要在当前目录(-r)中递归搜索(.),列出不匹配(-L)多行的文件({ {1}})在文件末尾查找换行符的正则表达式(-M

'\n$'更改为-L会列出 中包含换行符的文件。

可以使用自制-l包在MacOS上安装

pcregreppcre

答案 1 :(得分:17)

好的,轮到我了,我试一试:

find -type f -print0 | xargs -0 -L1 bash -c 'test "$(tail -c 1 "$0")" && echo "No new line at end of $0"'

答案 2 :(得分:8)

尝试一下:

find -type f -exec sh -c '[ -z "$(sed -n "\$p" "$1")" ]' _ {} \; -print

它将打印以空行结尾的文件的文件名。要打印不以空行结尾的文件,请将-z更改为-n

答案 3 :(得分:3)

如果你使用'ack'(http://beyondgrep.com)作为grep的替代品,你只需运行:

ack -v '\n$'

它实际上会在行尾搜索所有不匹配的行(-v)换行符。

答案 4 :(得分:3)

如果您安装了ripgrep:

rg -l '[^\n]\z'

该正则表达式匹配的字符不是换行符,然后匹配the end of the file

答案 5 :(得分:2)

这是kludgy;肯定会有人做得更好:

for f in `find . -name '*.txt' -type f`; do
    if test `tail -c 1 "$f" | od -c | head -n 1 | tail -c 3` != \\n; then
        echo $f;
    fi
done

N.B。这回答了标题中的问题,这与正文中的问题不同(正在查找以\ n \ n结尾的文件)。

答案 6 :(得分:2)

这应该可以解决问题:

#!/bin/bash

for file in `find $1 -type f -name "*.txt"`;
do
        nlines=`tail -n 1 $file | grep '^$' | wc -l`
        if [ $nlines -eq 1 ]
                then echo $file
        fi
done;

这样称呼:./script dir

E.g。 ./script /home/user/Documents/ - >列出/home/user/Documents中以\n结尾的所有文本文件。

答案 7 :(得分:2)

此页面上的大多数解决方案对我不起作用(FreeBSD 10.3 amd64)。伊恩威尔 OSX解决方案几乎总是有效,但很难遵循: - (

有一个简单的解决方案,几乎总是有效:(如果$ f是文件):

sed -i'' -e' $ a \' " $ F"

sed解决方案存在一个主要问题:它永远不会给你 只需检查(而不是附加换行符)的机会。

上述两种解决方案都无法用于DOS文件。我认为最多 便携/可编写脚本的解决方案可能是最简单的解决方案 我自己开发的: - )

这是基本的sh脚本,它结合了file / unix2dos / tail。在 生产,你可能需要使用" $ f"在引号和获取尾输出 (嵌入到名为last的shell变量中)为\" $ f \"

if file $f | grep 'ASCII text' > /dev/null; then
    if file $f | grep 'CRLF' > /dev/null; then
        type unix2dos > /dev/null || exit 1
        dos2unix $f
        last="`tail -c1 $f`"
        [ -n "$last" ] && echo >> $f
        unix2dos $f
    else
        last="`tail -c1 $f`"
        [ -n "$last" ] && echo >> $f
    fi
fi

希望这有助于某人。

答案 8 :(得分:1)

另一种选择:

$ find . -name "*.txt" -print0 | xargs -0I {} bash -c '[ -z "$(tail -n 1 {})" ] && echo {}'

答案 9 :(得分:1)

由于你的问题有perl标签,我会发一个使用它的答案:

find . -type f -name '*.txt' -exec perl check.pl {} +

其中check.pl如下:

#!/bin/perl 

use strict;
use warnings;

foreach (@ARGV) {
    open(FILE, $_);

    seek(FILE, -2, 2);

    my $c;

    read(FILE,$c,1);
    if ( $c ne "\n" ) {
        print "$_\n";
    }
    close(FILE);
}

这个perl脚本只是打开,每次一个,作为参数传递的文件,只读取倒数第二个字符;如果它不是换行符,它只打印出文件名,否则它什么都不做。

答案 10 :(得分:1)

这个例子

  • 适用于 macOS (BSD) 和 GNU/Linux
  • 使用标准工具:find、grep、sh、file、tail、od、tr
  • 支持带空格的路径

单线:

find . -type f -exec sh -c 'file -b "{}" | grep -q text' \; -exec sh -c '[ "$(tail -c 1 "{}" | od -An -a | tr -d "[:space:]")" != "nl" ]' \; -print

更具可读性的版本

  • 在当前目录下查找
    • 常规文件
    • 那个“文件”(简要模式)考虑文本
    • 谁的最后一个字节(tail -c 1)没有用od的命名字符“nl”表示
    • 并打印它们的路径
#!/bin/sh
find . \
    -type f \
    -exec sh -c 'file -b "{}" | grep -q text' \; \
    -exec sh -c '[ "$(tail -c 1 "{}" | od -An -a | tr -d "[:space:]")" != "nl" ]' \; \
    -print

最后,一个带有 -f 标志的版本来修复有问题的文件(需要 bash)。

#!/bin/bash
# Finds files without final newlines
# Pass "-f" to also fix those files
fix_flag="$([ "$1" == "-f" ] && echo -true || echo -false)"
find . \
    -type f \
    -exec sh -c 'file -b "{}" | grep -q text' \; \
    -exec sh -c '[ "$(tail -c 1 "{}" | od -An -a | tr -d "[:space:]")" != "nl" ]' \; \
    -print \
    $fix_flag \
    -exec sh -c 'echo >> "{}"' \;

答案 11 :(得分:0)

我能想到的最好的单线是:

git grep --cached -Il '' | xargs -L1 bash -c 'if test "$(tail -c 1 "$0")"; then echo "No new line at end of $0"; exit 1; fi'

这使用git grep,因为在我的用例中,我想确保提交到git分支的文件以换行符结尾。

如果这在git repo之外是必需的,那么您当然可以只使用grep

grep -RIl '' . | xargs -L1 bash -c 'if test "$(tail -c 1 "$0")"; then echo "No new line at end of $0"; exit 1; fi'

为什么我要使用grep?因为您可以使用-I轻松过滤出二进制文件。

然后在其他答案中找到通常的xargs / tailthing,如果文件没有换行符,则添加以1退出。因此,可以将其用于预提交的githook或CI。

答案 12 :(得分:0)

这是另一个使用小 bash 内置命令的示例,其中:

  • 允许您过滤扩展名(例如,| grep '\.md$' 只过滤 md 文件)
  • 管道更多 grep 命令用于扩展过滤器(如排除 | grep -v '\.git' 以排除 .git 下的文件
  • 使用 grep 参数的全部功能来创建更多过滤器或包含物

代码基本上迭代 (for) 所有文件(匹配您选择的条件 grep),如果文件的最后 1 个字符 (-n "$(tail -c -1 "$file")") 不是空白行,它将打印文件名(echo "$file")。

详细代码:

for file in $(find . | grep '\.md$')
do
    if [ -n "$(tail -c -1 "$file")" ]
    then
        echo "$file"
    fi
done

更紧凑一点:

for file in $(find . | grep '\.md$')
do
    [ -n "$(tail -c -1 "$file")" ] && echo "$file"
done

当然还有它的 1-liner:

for file in $(find . | grep '\.md$'); do [ -n "$(tail -c -1 "$file")" ] && echo "$file"; done