Wie finde ich Dateien mit 100% NUL-Zeichen in ihrem Inhalt?

14

Was ist der Linux-Befehlszeilenbefehl, der solche Dateien identifizieren kann?

AFAIK der findBefehl (oder grep) kann nur übereinstimmen einer bestimmten Zeichenfolge in der Textdatei . Aber ich möchte den gesamten Inhalt abgleichen, dh ich möchte sehen, welche Dateien mit dem regulären Ausdruck übereinstimmen \0+, wobei die Zeilenendezeichen ignoriert werden . Vielleicht könnte das find . cat | grepIdiom funktionieren, aber ich weiß nicht, wie ich grep dazu bringen kann, Zeilen zu ignorieren (und die Datei als binär zu behandeln).

Hintergrund: Alle paar Tage, wenn mein Laptop einfriert, verliert meine btrfs-Partition Informationen: Zum Schreiben geöffnete Dateien werden durch Nullen ersetzt (die Größe der Datei bleibt mehr oder weniger intakt). Ich verwende die Synchronisation und möchte nicht, dass sich diese gefälschten Dateien verbreiten. Ich brauche eine Möglichkeit, sie zu identifizieren, damit ich sie aus dem Backup holen kann.

Adam Ryczkowski
quelle
Meinen Sie Dateien mit numerischen Nullen?
Rahul Patil
1
Ich denke, es geht eher um NULL-Zeichen als um numerische Nullen.
Gertvdijk
9
Machen wir einen Schritt zurück. Alle paar Tage, wenn Ihr Laptop einfriert? Warum nicht versuchen wir zu beheben , dass hier das eigentliche Problem?
D_Bye
2
@D_Bye das ist eine gute Idee, aber bisher ist es nicht zu weit gekommen: [ unix.stackexchange.com/questions/57894/…
Adam Ryczkowski
1
Haben Sie die -vOption für grep in Betracht gezogen : Filtern Sie alle Dateien mit den Bytes 1 bis 255 heraus.
ctrl-alt-delor

Antworten:

10

Sie können grepfür ␀ Zeichen den Perl-Regex-Modus verwenden:

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

So können Sie dies verwenden:

for path in *.foo
do
    grep -P "[^\0]" "$path" || echo "$path"
done
l0b0
quelle
Ich erhalte unerwartete Ergebnisse mit GNU grep 2.5.4. Unabhängig davon, ob ich --binary-files=textoder verwende --binary-files=binary, gibt es truefür alle nicht leeren Datenwerte ein Ergebnis, z. "\0\0", "\0x\0", "abcd"... Der genaue Code , den ich verwendet wird: 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
Ich habe es jetzt weiter versucht GNU grep) 2.10. Diese spätere Version liefert die erwarteten Ergebnisse ... also einen verspäteten +1
Peter.O
Schlägt bei einer mit printf '\0\n\0\0\n\n' > fileoder printf '\n' > filefür diesen Zweck erstellten Datei fehl.
Stéphane Chazelas
@ StéphaneChazelas OP hat "Ignorieren der Zeilenende-Zeichen" gesagt. Daher ist jede Datei, die nur aus \0und \nZeichen besteht (auch keine von beiden), eine Übereinstimmung.
l0b0
5

Ich stimme dem zu, was D_Bye über das Finden der Problemwurzel sagt.

Wie auch immer, um zu überprüfen, ob eine Datei nur Folgendes enthält \0und / oder verwenden \nkönnte tr:

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

Wodurch 0 für null / newline und leere Dateien zurückgegeben wird.

Thor
quelle
2
tr -d '\0\n'behebt das Newline-Problem, bei dem nur das Problem (?) leerer Dateien in der Ausgabe aufgelistet bleibt ... Es verarbeitet jedoch jedes Byte jeder Datei (was möglicherweise ein Problem ist oder nicht) +1
Peter.O
@ Peter.O: Ich habe die Newline-Anforderung verpasst, danke. Diese Lösung ist nicht sehr optimiert, und wenn sie mit vielen Daten ausgeführt werden soll, ist es besser, eine Lösung zu verwenden, bei der nicht übereinstimmende Bytes gefunden werden.
Thor
Es funktioniert sehr gut. In meinem Fall musste ich nur sicherstellen, dass keine Dateien mit der Länge 0 ausgeschlossen wurden. Vielen Dank.
Adam Ryczkowski
1
Dies zählt jedoch auch Dateien mit Zeilenumbrüchen als "leer".
Chris Down
@ ChrisDown: Ich habe den Antworttext klargestellt, was er macht. Es ist nicht klar, was das OP mit reinen Newline-Dateien machen möchte.
Thor
4

Hier ist ein kleines Python-Programm, das das kann:

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])))

Und in Aktion:

$ 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

Sie können mehrere Dateien überprüfen , indem Sie finden die Verwendung von -exec, xargs, GNU parallel, und ähnliche Programme. Alternativ werden Dateinamen gedruckt, die behandelt werden müssen:

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

Denken Sie daran, dass Dateinamen, wenn Sie die Ausgabe dieses Programms an ein anderes Programm übergeben, Zeilenumbrüche enthalten können \0 ).

Wenn Sie viele Dateien haben, ist es besser, eine Option für die parallele Verarbeitung zu verwenden, da nur jeweils eine Datei gelesen wird.

Chris Down
quelle
4

Ich vermute, diese Dateien sind spärlich, das heißt, ihnen ist kein Speicherplatz zugewiesen. Sie geben lediglich eine Dateigröße an (du würden 0 für sie melden).

In diesem Fall könnten Sie mit GNU find Folgendes tun (vorausgesetzt, kein Dateipfad enthält Zeilenumbruchzeichen):

find . -type f -size +0 -printf '%b:%p\n' | grep '^0:' | cut -d: -f2-
Stéphane Chazelas
quelle
Guter Punkt. Ich habe niemals darüber nachgedacht. Ich werde es versuchen. Durch dudie Verwendung von wird verhindert, dass der Inhalt jeder einzelnen Datei im Dateisystem zerkratzt wird, sodass der gesamte Vorgang nicht länger als 30 Minuten dauert.
Adam Ryczkowski
(und printf %bdarüber berichtet, was berichten duwürde)
Stéphane Chazelas
3

Suchen Sie nach Dateien, die nur Null-Zeichen '\ 0' und Zeilenumbrüche '\ n' enthalten.
Die qin sed Ursachen jede Datei sofort zu beenden sucht auf der Suche nach jedem Nicht-Null - Zeichen in einer Zeile.

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

Testdateien erstellen

> 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

Ausgabe

./file-with-nulls-and-newlines
./file-with-nulls-only
Peter.O
quelle
0

Python

Einzelne Datei

Definieren Sie den Alias:

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

Probier es aus:

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

Mehrere Dateien

Suche alle Binärdateien rekursiv:

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

Um alle nicht-binären Dateien zu finden, ändern Sie &&mit ||.

Kenorb
quelle
0

Zur Verwendung von GNU sed können Sie die -zOption verwenden, die eine Zeile als nullterminierte Zeichenfolgen definiert und leere Zeilen wie folgt sucht und löscht:

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

Der Kopfbefehl dazwischen ist nur eine Optimierung.

mxmlnkn
quelle