web-dev-qa-db-ja.com

コンテンツに100%NUL文字が含まれるファイルを見つける方法は?

そのようなファイルを特定できるLinuxコマンドラインコマンドとは何ですか?

findコマンド(またはgrep)はmatchテキストファイル内の特定の文字列のみを認識できます。しかし、コンテンツ全体を照合したいのです。つまり、どのファイルが正規表現\0+に一致するかを確認したいのですが、行末の文字を無視してです。多分find . cat | grepイディオムはうまくいくかもしれませんが、grepを無視して行を無視する方法(そしてファイルをバイナリとして扱う方法)がわかりません。

背景:数日おきにラップトップがフリーズすると、btrfsパーティションの情報が失われます。書き込み用に開かれたファイルの内容はゼロに置き換えられます(ファイルのサイズはほぼそのままです)。私は同期を使用しており、これらの偽のファイルを伝播させたくありません。バックアップから取得できるように、それらを識別する方法が必要です。

16
Adam Ryczkowski

Perl正規表現モードを使用して、␀文字に対してgrepを実行できます。

$ echo -ne "\0\0" > nul.bin
$ echo -ne "\0x\0" > non-nul.bin
$ grep -P "[^\0]" *.bin
Binary file non-nul.bin matches

だからあなたはこれを使うことができます:

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done
10
l0b0

問題の根本を見つけることについてD_Byeが言っていることに同意します。

とにかく、ファイルに\0または\nのみが含まれているかどうかを確認するには、trを使用できます。

<file tr -d '\0\n' | wc -c

Null/newlineおよび空のファイルに対しては0を返します。

6
Thor

これらのファイルはスパースであると思われます。つまり、ファイルにディスクスペースが割り当てられておらず、ファイルサイズを指定しているだけです(duは0を報告します)。

その場合、GNU findを使用すると、次のようにできます(ファイルパスに改行文字が含まれていないと仮定)。

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-
5

これができる小さなpythonプログラムです:

import sys

def only_contains_nulls(fobj, chunk_size=1024):
    first = True
    while True:
        data = fobj.read(chunk_size)
        if not data:
            if first:
                return 1  # No data
            else:
                return 0
        if data.strip("\0"):
            return 1
        first = False

if __name__ == '__main__':
    with open(sys.argv[1]) as f:
        sys.exit(only_contains_nulls(f))

そして実際に:

$ printf '\0\0\0' > file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Only nulls
$ printf a >> file
$ ./onlynulls file && echo "Only nulls" || echo "Non-null characters"
Non-null characters

Findの-execxargs、GNU parallel、および同様のプログラムを使用して、複数のファイルをチェックできます。または、必要なファイル名を出力します対処する:

files=( file1 file2 )
for file in "${files[@]}"; do
    ./onlynulls "$file" || printf '%s\n' "$file"
done

この出力を別のプログラムに渡す場合は、ファイル名に改行を含めることができるため、別の方法で区切る必要があります(\0で適切に)。

多数のファイルがある場合は、一度に1つのファイルしか読み取らないため、並列処理のオプションを使用することをお勧めします。

4
Chris Down

このワンライナーは、GNU findxargs、およびgrepを使用して100%nulファイルを見つける最も効率的な方法です後者はPCREサポートで構築されています):

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00]" --

他の提供された回答に対するこの方法の利点は次のとおりです。

  • 非スパースファイルが検索に含まれます。
  • 読み取り不可能なファイルはgrepに渡されないため、Permission denied警告は回避されます。
  • grepは、null以外のバイトを見つけた後、ファイルからのデータの読み取りを停止します(LC_ALL=Cは、各byteを確認するために使用されます) 文字)として解釈されます。
  • 空のファイル(ゼロバイト)は結果に含まれません。
  • 複数のファイルを効率的にチェックするgrepプロセスが少なくなります。
  • 改行を含む、または-で始まるパスは正しく処理されます。
  • python/Perlが不足しているほとんどの組み込みシステムで動作します。

-Zオプションをgrepに渡し、xargs -r0 ...を使用すると、100%nulファイルでさらにアクションを実行できます(例:クリーンアップ):

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00]" -- |
  xargs -r0 rm --

また、findオプション-Pを使用してシンボリックリンクの追跡を回避し、-xdevを使用してファイルシステムのトラバースを回避することもお勧めします(リモートマウント、デバイスツリー、バインドマウントなど)。

行末文字を無視する場合の場合、次のバリアントが機能するはずです(これはそれほど良い考えではないと思います)。

find . -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -r0 grep -LP "[^\x00\r\n]" --

バックアップを防止するために不要なファイル(nul /改行文字100%)を削除することを含め、すべてをまとめます。

find -P . -xdev -type f -size +0 -readable -print0 |
  LC_ALL=C xargs -0 grep -ZLP "[^\x00\r\n]" -- |
  xargs -0 rm --

空のファイル(ゼロバイト)を含めることはお勧めしません。それらは veryspecificpurposes でしばしば存在します。

3
Tyson

Null-chars '\ 0'と改行文字 '\ n'のみを含むファイルを検索します。
sedqを使用すると、行内でnull以外の文字が見つかると、各ファイル検索がすぐに終了します。

find -type f -name 'file-*' |
  while IFS= read -r file ;do 
      out=$(sed -n '1=; /^\x00\+$/d; i non-null
                      ; q' "$file")
      [[ $out == "1" ]] &&  echo "$file"
  done

テストファイルを作成する

> file-empty
printf '%s\n' 'line1' 'line2' 'line3'      > file-with-text           
printf '%4s\n' '' '' xx | sed 's/ /\x00/g' > file-with-text-and-nulls
printf '%4s\n' '' '' '' | sed 's/ /\x00/g' > file-with-nulls-and-newlines
printf '%4s'   '' '' '' | sed 's/ /\x00/g' > file-with-nulls-only

出力

./file-with-nulls-and-newlines
./file-with-nulls-only
3
Peter.O

Python

単一ファイル

エイリアスを定義します。

alias is_binary="python -c 'import sys; sys.exit(not b\"\x00\" in open(sys.argv[1], \"rb\").read())'"

試して:

$ is_binary /etc/hosts; echo $?
1
$ is_binary `which which`; echo $?
0

複数のファイル

すべてのバイナリファイルを再帰的に検索します。

IS_BINARY='import sys; sys.exit(not b"\x00" in open(sys.argv[1], "rb").read())'
find . -type f -exec bash -c "python -c '$IS_BINARY' {} && echo {}" \;

すべての非バイナリファイルを検索するには、&&||

0
kenorb

GNU sedを使用する場合、-zオプション。これは、行をゼロで終了する文字列として定義し、次のように空行を照合して削除します。

if [ "$( sed -z '/^$/d' "$file" | head -c 1 | wc -c )" -eq 0 ]; then
    echo "$file contains only NULL!"
fi

その間のヘッドコマンドは、最適化にすぎません。

0
mxmlnkn