Identifizieren Sie Dateien mit Nicht-ASCII- oder nicht druckbaren Zeichen im Dateinamen

24

In einem Verzeichnis mit einer Größe von 80 GB und ungefähr 700.000 Dateien sind einige Dateinamen mit nicht englischen Zeichen im Dateinamen enthalten. Anders als das mühsame Durchsuchen der Dateiliste gibt es:

  • Eine einfache Möglichkeit, diese Dateinamen aufzulisten oder anderweitig zu identifizieren?
  • Eine Möglichkeit, nicht englischsprachige druckbare Zeichen zu generieren - die Zeichen, die nicht im druckbaren Bereich von aufgeführt sind man ascii(damit ich testen kann, ob diese Dateien identifiziert werden)?
Verdächtiger
quelle

Antworten:

32

Unter der Annahme, dass "fremd" "kein ASCII-Zeichen" bedeutet, können Sie findmit einem Muster alle Dateien suchen, deren Name keine druckbaren ASCII-Zeichen enthält:

LC_ALL=C find . -name '*[! -~]*'

(Das Leerzeichen ist das erste druckbare Zeichen auf http://www.asciitable.com/ und ~das letzte.)

Der Hinweis für LC_ALL=Cist erforderlich (tatsächlich LC_CTYPE=Cund LC_COLLATE=C), andernfalls wird der Zeichenbereich falsch interpretiert. Siehe auch die Handbuchseite glob(7). Da LC_ALL=CUrsachen findStrings als ASCII zu interpretieren, wird es Multi-Byte - Zeichen (wie gedruckt π) als Fragezeichen. Um dies zu beheben, catleiten Sie eine Pipe an ein Programm (z. B. ) oder leiten Sie in eine Datei um.

Anstatt Zeichenbereiche anzugeben, [:print:]können auch "druckbare Zeichen" ausgewählt werden. Stellen Sie sicher, dass Sie das Gebietsschema C festlegen, da Sie sonst ein (scheinbar) willkürliches Verhalten erhalten.

Beispiel:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π
Lekensteyn
quelle
1
Beachten Sie, dass Sie Dateinamen haben, die fremde Zeichensätze verwenden, die nicht mit UTF-8 oder ASCII kompatibel sind. In diesen Fällen werden möglicherweise Fragezeichen anstelle von Zeichen angezeigt.
Lekensteyn
1
+1, aber ich würde LC_ALL=Canstelle von verwenden, LC_COLLATE=Cda es nicht sehr sinnvoll ist, LC_COLLATE ohne Einstellung auf C zu setzen LC_CTYPEund sicherzustellen, dass es auch dann noch funktioniert, wenn sich die Variable LC_ALL in der Umgebung befindet.
Stéphane Chazelas
Wenn SPCes druckbar ist , was ist dann damit TABund LFwelche sind in der Regel auch in Textdateien zu finden?
Stéphane Chazelas
1
Danke - es wurden sechs Dateien gefunden, die einen langen Bindestrich, einen kurzen Bindestrich und eine Variante mit einfachen Anführungszeichen enthielten. Diese stammten alle aus MS Word. Kein Unterschied zwischen den aufgelisteten Dateien LC_ALL und LC_COLLATE. LC_COLLATE zeigte die Nicht-ASCII-Zeichen korrekt an, während LC_ALL angezeigt wurde ??? stattdessen. Hervorragende Antwort!
Verdächtiger
1
@suspectus habe ich durch antworten auf vorschläge von stephane aktualisiert. Für LC_COLLATEund LC_CTYPEsiehe auch die find(1)Manpage.
Lekensteyn
6

Wenn Sie jeden Dateinamen mit übersetzen tr -d '[\200-\377]'und mit dem ursprünglichen Namen vergleichen, stimmen alle Dateinamen mit Sonderzeichen nicht überein.

(Die obige Annahme, dass Sie Nicht-ASCII mit fremd bedeuten)

Timo
quelle
2
Das entfernt auch [und ]in den meisten trImplementierungen.
Stéphane Chazelas
Ja - es wurde entfernt [und ]auf meinem System.
Verdächtiger
+1 - Die Lösung hat alle (sechs) Dateinamen mit Nicht-ASCII-Symbolen (zusätzlich zu [und ]s) gefunden. Vielen Dank.
Verdächtiger
3

Sie können ein trbeliebiges Fremdzeichen aus einem Dateinamen löschen und das Ergebnis mit dem Originaldateinamen vergleichen, um festzustellen, ob es Fremdzeichen enthält.

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames
Ernest A
quelle
4
das eine nette Erweiterung auf meine Antwort ist, aber es ist zu einfach, kann Dateinamen haben Zeilenumbrüche in ihnen und dann das Skript wird nicht funktionieren
Timo
1
Wenn Sie die findAusgabe nachbearbeiten möchten , verwenden Sie die NUL-terminierte Ausgabe / Eingabe, wie in dieser Antwort gezeigt .
Lekensteyn
0

Die akzeptierte Antwort ist hilfreich, aber wenn sich Ihre Dateinamen bereits in der in LANG/ angegebenen Kodierung befinden LC_CTYPE, ist es besser, einfach Folgendes zu tun:

LC_COLLATE=C find . -name '*[! -~]*'

Zeichenklassen sind betroffen LC_CTYPE, der obige Befehl verwendet jedoch keine Zeichenklassen, sondern nur Bereiche, sodass LC_CTYPEnur verhindert wird , dass ungewöhnliche Zeichen durch Fragezeichen ersetzt werden.

SamB
quelle