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


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が「ほとんど黙って」とは、例えば、1を追加します)、およびいくつかの(テキスト指向)コマンドは無視されます改行で終わっていない場合は最後の行(wc、iirc ....ですが、他にもあります)。そして、これは役立つかもしれません
Olivier Dulac

awk 'END{print}' $file :これは$ fileの内容を完全に無視し、 "$ file"に含まれるすべてのファイルの解析が終了した後、改行を追加します。これはawkコマンドが出力する唯一のものであるため、:printf '\n'($ fileのmentinoなしで)に置き換えて、同じことを行うことができます。これはあなたが目指していたものではないと思います(つまり:ファイルの最後の行を印刷しますか?)
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

回答:


14

明確にするために、LF(別名\nまたは改行)文字は行区切り文字であり、行区切り文字ではありません。改行文字で終了しない限り、行は終了しません。だけが含まれてa\nbいるファイルは、最終行の後に文字が含まれているため、有効なテキストファイルではありません。のみを含むファイルでも同じですa。を含むファイルにa\n空でない行が1つ含まれています。

したがって、少なくとも1つの空行で終わるファイルは、2つの改行文字で終わるか、1つの改行文字を含みます。

次の場合:

 tail -c 2 file | od -An -vtc

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

空行で終わるファイルを見つけるためにこれを使用するには、ファイルの最後の2バイトのみを読み取るという点で(特に大きなファイルの場合)効率的ですが、最初は特に、の実装ごとに一貫性がなくod、ファイルごとに1 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
}

いくつかのファイル(s)がテキストファイルであれば知っているこの答えのメソッドを使用する方法: 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(またはbashshopt -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 -c2してくださいtail -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 2016年

ああ、改行文字は無視されます。
Oskar Skog 2016年

より読みやすくcat $file 2>&1 /dev/nullするか、これがBashのみの場合は考慮してくださいcat $file &> /dev/null

1
また、$file使用されているすべての箇所を引用することを検討してください。$(commands ...)代わりに`backticks`...を使用してください
cat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.