我在当前目录的子目录中有一些文件,它们的末尾可能有换行符,也可能没有换行符;如何找到末尾没有换行符的文件?
我试过这个:
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
。
答案1
澄清一下,LF(又名\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}' {} +
(查找以空行结尾的文件)将运行尽可能少的命令,但这意味着读取所有文件的完整内容。
理想情况下,您需要一个能够自行读取文件末尾的 shell。
和zsh
:
zmodload zsh/system
for f (**/*(D.L+0)) {
{
sysseek -w end -2
sysread
[[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
} < $f
}
答案2
使用gnu sed
和 类似的外壳zsh
(或bash
使用shopt -s globstar
):
sed -ns '${/./F}' ./**/*.styl
这会检查每个文件的最后一行是否不为空,如果是,则打印文件名。
如果您想要相反的结果(如果最后一行为空则打印文件名)只需替换/./
为/^$/
答案3
正确终止的文本文件最后一行为空,以两个\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 -c2
为tail -c4
.