最後に空行がないファイルを見つける方法は?

最後に空行がないファイルを見つける方法は?

現在、ディレクトリのサブディレクトリには末尾に改行文字がある場合とない場合があるファイルがあります。最後に改行文字のないファイルを見つける方法は?

私はこれを試しました:

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最後の行の後に文字が含まれているため、有効なテキストファイルではありません。空でない行が1つだけ含まれるファイルの場合aa\n

したがって、1 つ以上の空行で終わるファイルには、2 つの改行で終わるか、1 つの改行が含まれます。

場合:

 tail -c 2 file | od -An -vtc

出力\nまたは\n \nファイルには、少なくとも1つの末尾の空行が含まれています。何も出力しない場合は空のファイルで、出力すると<anything-but-\0> \n空でない行で終わります。それ以外はテキストファイルではありません。

これで空の行で終わるファイルを見つけるには、ファイルの最後の2バイトのみを読み取るために機能します(特に大容量ファイルの場合)。しかし、まず、出力をプログラムで解析するのは簡単ではありません。ある実装から次の実装まで一貫性がないため、各ファイルに対して1つずつod実行する必要があります。tailod

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
}

答え2

gnu sedと同様のシェルを使用してくださいzsh(またはbash使用shopt -s globstar):

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

各ファイルの最後の行が空でないことを確認し、空の場合はファイル名を印刷します。
反対の結果が必要な場合(最後の行が空の場合はファイル名を印刷してください)、次の/./ように置き換えます。/^$/

答え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 -c2に変更しますtail -c4

関連情報