如何查找末尾没有空行的文件?


9

我在当前目录的子目录中有文件,文件末尾可能没有换行;如何找到结尾没有换行符的文件?

我已经试过了:

find . -name '*.styl' | while read file; do
    awk 'END{print}' $file | grep -E '^$' > /dev/null || echo $file;
done

但这不起作用。 awk 'END{print}' $file与一样在空的新行之前打印该行tail -n 1 $file


@don_crissti我需要没有尾随空行的文件。
jcubic

2
请问您需要查找那些文件的原因吗?我猜这与以下事实有关:Unix中的文本文件应该以换行符终止(例如,保存时vi将“几乎无声地”添加一个),并且多个(面向文本的)命令将忽略最后一行,如果它不是由换行符终止(wc,iirc ....但还有其他)。而可能会帮助
奥利维尔·杜拉克

awk 'END{print}' $file :这将完全忽略$ file的内容,并且在解析完“ $ file”中包含的所有文件后,将添加换行符。因为它是awk命令打印的唯一内容,所以可以将其替换为:(printf '\n'完全没有$ file的任何提示),并执行相同的操作。我认为这不是您的目标(即:打印文件的最后一行吗?)
Olivier Dulac

@don_crissti:如果文件的最后一个字符不是换行符,则该文件不是严格地是unix TEXT文件。参见:unix.stackexchange.com/a/263919/27616。请注意,如果没有以换行符终止的话,许多文本命令(例如wc)只是忽略了最后的“行”
Olivier Dulac

1
@OlivierDulac:gawk可以打印c,FreeBSD也可以打印,但是我没有注意到它被记录为依赖于实现的文件:gnu.org/software/gawk/manual/…。因此确实发生了,但并非总是如此。
dave_thompson_085 '16

Answers:


14

需要说明的是,LF(aka \n或换行符)字符是行定界符,而不是行分隔符。除非以换行符结尾,否则行不会结束。仅包含的文件a\nb不是有效的文本文件,因为它在最后一行之后包含字符。与仅包含的文件相同a。一个包含文件a\n中包含一个非空行。

因此,以至少一个空行结尾的文件以两个换行符结尾或包含一个换行符。

如果:

 tail -c 2 file | od -An -vtc

输出\n\n \n,则文件包含至少一个尾随空行。如果不输出任何内容,则为空文件;如果输出<anything-but-\0> \n,则以非空行结尾。除此之外,它不是文本文件。

现在,要使用它来查找以空行结尾的文件,可以这样做是有效的(特别是对于大文件),因为它仅读取文件的最后两个字节,但是首先,不容易以编程方式解析输出,尤其是考虑到从一个实现od到下一个实现是不一致的,我们需要为每个文件运行一个tail和一个od

find . -type f -size +0 -exec gawk '
  ENDFILE{if ($0 == "") print FILENAME}' {} +

(查找以空行结尾的文件)将运行尽可能少的命令,但这意味着读取所有文件的全部内容。

理想情况下,您需要一个可以自己读取文件末尾的外壳。

zsh

zmodload zsh/system
for f (**/*(D.L+0)) {
  {
    sysseek -w end -2
    sysread
    [[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
  } < $f
}

一种使用此答案的方法来了解某些文件是否为文本文件的方法: are_textfiles () { nontext=0; rem="return 0 if all args are files with terminating newline, or n [=number of non-textfiles]" ; for f in "$@" ; do [ -f "$f" ] && { tail -c 1 "$f" | od -An -vtc | grep "\\n" ;} >/dev/null 2>&1 || ((nontext++)) ; done ; return $nontext ; }。用作:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Olivier Dulac

6

使用gnu sed和壳zsh(或bash使用shopt -s globstar):

sed -ns '${/./F}' ./**/*.styl

这将检查每个文件的最后一行是否不为空,如果是,它将打印文件名。
如果要相反(如果最后一行为空,则打印文件名),只需替换/.//^$/


1
从未见过-s行动。谢谢GNU!
格伦·杰克曼

注意:F选项存在于sed版本4.2.2(2012年12月22日)
Isaac

3

正确终止的文本文件的最后一行为空,以2结尾\n

然后,我们希望该值tail -c2必须等于$'\n\n'

可悲的是,命令扩展会删除尾随的新行。我们将需要一些调整。

f=filename
nl='
'
t=$(tail -c2 $f; printf x)  # capture the last two characters.
r="${nl}${nl}$"                 # regex for: "ends in two newlines".
[[ ${t%x} =~ $r ]] &&  echo "file $f ends in an empty line"

我们甚至可以扩展一点以检查哪些文件的尾随新行失败:

nl='
'
nl=$'\n'
find . -type f -name '*.styl' | while read f; do
    t=$(tail -c2 $f; printf x); r1="${nl}$"; r2="${nl}${r1}"
    [[ ${t%x} =~ $r1 ]] || echo "file $f is missing a trailing newline"
    [[ ${t%x} =~ $r2 ]] && echo "$f"
done

请注意,$'\r\n如果需要,可以将换行符更改为类似的内容。
在这种情况下,也请更改tail -c2tail -c4


0
for file in *; do
    # Check if the file is readable to avoid clutter
    if cat "./$file" 2&>1 /dev/null; then
        # Compare the last character with a single newline character.
        if [ -n "$(tail -c 1 -- "./$file")" ]; then
            echo "$file"
        fi
        # Also report empty files.
        if [ $(wc -c  < "./$file") -eq 0 ]; then
            echo "$file"
        fi
    fi
done

1
这不适用于空文件,但我可以忍受。
jcubic

可能会有更多错误,因为字符串比较似乎无法按我预期的方式工作。我添加了一个空文件检查。
Oskar Skog

嗯,它会忽略换行符。
Oskar Skog

考虑更具可读性cat $file 2>&1 /dev/null,或者如果仅Bash的话cat $file &> /dev/null
2016年

1
此外,考虑报$file到处都是它的使用-和请使用$(commands ...)而不是`backticks`...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.