内容に100%NUL文字を含むファイルを見つける方法は?


16

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

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

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


あなたはその中に数値ゼロを持つファイルを意味しますか?
ラーフルパティル

2
数字のゼロではなく、NULL文字についてだと思います。
gertvdijk

10
ここから一歩戻りましょう。数日ごとに、ラップトップがフリーズしますか?ここで本当の問題である、なぜそれを修正しようとしていないのですか?
D_Bye

2
[:良いアイデアだが、これまでのところ、それはあまりにも遠く来なかった@D_Bye unix.stackexchange.com/questions/57894/...
アダム・ライチョースキー

1
-vgrep のオプションを検討しました
。1〜255

回答:


10

grepPerl正規表現モードを使用して␀文字を作成できます。

$ 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

を使用して、予期しない結果が得られGNU grep 2.5.4ます。かかわらず、私が使用しているかどうかの--binary-files=text--binary-files=binary、それが与えるtrueなど、すべての非空のデータ値のための結果を。"\0\0""\0x\0""abcd"...私は使用される正確なコードは次のとおりです。 for typ in binary text ;do for dat in '\0\0' '\0x\0' 'abcd' '' ;do printf "$dat" >f; grep --binary-files=$typ -P '[^\0]' f >/dev/null && echo true || echo false; done; done
Peter.O

1
私は今さらに試してみましたGNU grep) 2.10。この後のバージョンでは、期待どおりの結果が得られます...だから、遅れて+1
Peter.O

printf '\0\n\0\0\n\n' > fileまたはそのprintf '\n' > fileために作成されたファイルで失敗します。
ステファンシャゼル

1
@StéphaneChazelasOPは「行末文字を無視する」と言っていました。そのため、とだけ\0で構成されるファイル\n(いずれかがゼロであっても)は一致します。
l0b0

6

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

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

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

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


2
tr -d '\0\n'(?)を解き、その後唯一の問題は空のファイルの出力にリストされたままに改行の問題は、...それはしかし、すべてのファイルのすべてのバイトを処理しない(どのまたは問題であってもなくてもよい)1
Peter.O

@ Peter.O:改行の要件を逃しました、ありがとう。このソリューションはあまり最適化されておらず、大量のデータで実行する場合は、一致しないバイトが見つかったときに移動するソリューションの方が適しています。
トール

とてもうまくいきます。私の場合、長さゼロのファイルを除外することだけを確認する必要がありました。ありがとうございました。
アダムリツコフスキ

1
ただし、これは、改行を含むファイルを「空」としてカウントします。
クリスダウン

1
@ChrisDown:私はそれが何をするかについての回答テキストを明確にしました。OPが改行のみのファイルで何をしたいのかは明確ではありません。
トール

5

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

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

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

いい視点ね。考えもしなかった。私が試してみます。を使用duすると、ファイルシステム内のすべてのファイルの内容に傷が付くのを防ぐことができるため、手順全体が完了するまでに30分以上かかりません。
アダムリツコフスキ

(およびprintf %b上記の報告内容du
ステファンシャゼル

ゼロの長さのファイルが結果から除外さ-size +0れる-size +1ように変更します。また\n、パスに含まれるファイルは、このコマンドの問題を引き起こします。
タイソン

@Tyson -size +0は、厳密に0より大きいサイズ-size +1用です。厳密に512より大きいサイズ用です。改行の制限については既に述べました。
ステファンChazelas

4

これを実行できる小さなpythonプログラムを次に示します。

import sys
def chunkCheck(fileObject, chunkSize=1024):
    while True:
        data = fileObject.read(chunkSize)
        if not data:
            return False
        if data.strip("\0"):
            return True
sys.exit(chunkCheck(open(sys.argv[1])))

そして実際に:

$ 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

あなたは、検索者の使用して複数のファイルを確認することができ-execxargs、GNU parallel、および同様のプログラムを。あるいは、これは処理する必要があるファイル名を出力します:

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

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

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


1
用心、長さゼロのファイル(例:/etc/nologin~/.hushlogin.nomedia、...)この答えによって誤認されています。
タイソン

3

null文字 '\ 0'および改行文字 '\ n'のみを含むファイルを検索します。でsedの原因各ファイルには、行にnull以外の文字を見つけるとすぐに終了して検索します。
q

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

どちらの-print0引数がから欠落しているように見えるfindまたはIFS=一部を台無しにされています。意図した区切り文字は何ですか?
タイソン

2

このワンライナーは、GNU find、xargs、およびGNU grepを使用して100%のnulファイルを見つける最も効率的な方法です。

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

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

  • 非スパースファイルは検索に含まれます。
  • 読み取り不能なファイルはgrepに渡されないため、Permission denied警告が回避されます。
  • grep null以外のバイトを検出すると、ファイルからのデータの読み取りを停止します。
  • 空のファイル(ゼロバイト)は結果に含まれません。
  • より少ないgrepプロセスで複数のファイルを効率的にチェックします。
  • 改行を含むパスまたはで始まるパス-は正しく処理されます。
  • 結果xargsをさらに処理するために渡すことができます。
  • Python / Perlを持たないほとんどの組み込みシステムで動作します。

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

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

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

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

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

不要なファイル(100%NUL /改行文字)を削除してバックアップされないようにするなど、すべてをまとめます。

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

空のファイル(ゼロバイト)を含めることはお勧めしません。多くの場合、非常に 特定の 目的で存在します


非常に多くの選択肢の中で最速であることは大胆な主張です。ベンチマークを追加する場合は、回答を承認済みとしてマークします:-)
Adam Ryczkowski

このようなベンチマークは、さまざまなディスクサブシステムのパフォーマンスなど、多くの要因に依存します。
タイソン

0

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

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

中間のheadコマンドは最適化にすぎません。


-1

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 {}" \;

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


1
質問は、ヌル文字のみを含むファイルを識別するように要求しました(改行は無視します)。ここで与えられたPythonコードはヌル文字を含むファイルを識別します。
タイソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.