identificare i file con caratteri non ASCII o non stampabili nel nome file


24

In una directory di dimensioni 80 GB con circa 700.000 file, ci sono alcuni nomi di file con caratteri non inglesi nel nome del file. Oltre alla traversata laboriosa dell'elenco dei file c'è:

  • Un modo semplice per elencare o identificare in altro modo questi nomi di file?
  • Un modo per generare caratteri stampabili in lingua non inglese - quei caratteri che non sono elencati nell'intervallo stampabile di man ascii(così posso provare che questi file vengono identificati)?

Risposte:


32

Supponendo che "estraneo" significhi "non un carattere ASCII", è possibile utilizzare findun modello per trovare tutti i file che non hanno caratteri ASCII stampabili nei loro nomi:

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

(Lo spazio è il primo carattere stampabile elencato su http://www.asciitable.com/ , ~è l'ultimo.)

È richiesto il suggerimento per LC_ALL=C(effettivamente LC_CTYPE=Ce LC_COLLATE=C), altrimenti l'intervallo di caratteri viene interpretato in modo errato. Vedi anche la pagina del manuale glob(7). Poiché LC_ALL=Ccausa findl'interpretazione delle stringhe come ASCII, stamperà caratteri multi-byte (come π) come punti interrogativi. Per risolvere questo problema, reindirizza a un programma (ad esempio cat) o reindirizza al file.

Invece di specificare intervalli di caratteri, [:print:] può anche essere utilizzato per selezionare "caratteri stampabili". Assicurati di impostare la locale C o ottieni un comportamento abbastanza (apparentemente) arbitrario.

Esempio:

$ 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
./π

1
Tenere presente che esistono nomi di file che utilizzano set di caratteri estranei incompatibili con UTF-8 o ASCII. In questi casi, potresti visualizzare punti interrogativi anziché caratteri.
Lekensteyn,

1
+1, ma userei LC_ALL=Cinvece LC_COLLATE=Cperché non ha molto senso impostare LC_COLLATE su C senza impostazione LC_CTYPEe assicurarsi che funzioni ancora anche quando la variabile LC_ALL si trova nell'ambiente.
Stéphane Chazelas,

Se SPCè stampabile , allora che dire TABe LFche si trovano in genere anche nei file di testo?
Stéphane Chazelas,

1
Grazie: sono stati trovati sei file con trattino lungo, trattino breve e una variante a virgoletta singola. Questi erano tutti originati da MS Word. Nessuna differenza nei file elencati tra LC_ALL e LC_COLLATE. LC_COLLATE ha visualizzato correttamente i caratteri non ASCII mentre LC_ALL ha visualizzato ??? anziché. Risposta eccellente!
suspectus,

1
@suspectus Ho aggiornato per risposta in base ai suggerimenti di Stephane. Per LC_COLLATEe LC_CTYPE, vedere anche la find(1)manpage.
Lekensteyn,

6

Se traduci ciascun nome di file utilizzando tr -d '[\200-\377]'e confrontalo con il nome originale, tutti i nomi di file con caratteri speciali non saranno gli stessi.

(Quanto sopra presupponendo che si intende non ASCII con estero)


2
Ciò rimuove anche [e ]nella maggior parte delle trimplementazioni.
Stéphane Chazelas,

Sì, è stato rimosso [e ]sul mio sistema.
suspectus,

+1: la soluzione ha trovato tutti i (sei) nomi di file con simboli non ASCII (oltre a [e ]s). Grazie.
suspectus,

3

È possibile utilizzare trper eliminare qualsiasi carattere estraneo da un nome file e confrontare il risultato con il nome file originale per vedere se conteneva caratteri estranei.

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

4
questa è una buona estensione alla mia risposta, ma è troppo semplice, i nomi dei file possono contenere delle righe e quindi il tuo script non funzionerà
Timo

1
Se si desidera post-elaborazione finddell'output, utilizzare output / input con terminazione NUL, come mostrato in questa risposta .
Lekensteyn,

0

La risposta accettata è utile, ma se i nomi dei file sono già nella codifica specificata in LANG/ LC_CTYPE, è meglio fare semplicemente:

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

Le classi di caratteri sono interessate LC_CTYPE, ma il comando precedente non utilizza classi di caratteri, ma solo intervalli, quindi LC_CTYPEimpedisce che i caratteri insoliti vengano sostituiti da punti interrogativi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.