identyfikuj pliki ze znakami spoza ASCII lub niedrukowalnymi w nazwie pliku


24

W katalogu o rozmiarze 80 GB z około 700 000 plików w nazwie pliku znajduje się kilka nazw ze znakami w języku innym niż angielski. Oprócz pracochłonnego przeszukiwania listy plików istnieje:

  • Łatwy sposób na wylistowanie lub inną identyfikację tych nazw plików?
  • Sposób generowania drukowalnych znaków w języku innym niż angielski - te znaki, które nie są wymienione w zakresie do wydrukowania man ascii(więc mogę przetestować, czy te pliki są identyfikowane)?

Odpowiedzi:


32

Zakładając, że „obcy” oznacza „nie znak ASCII”, możesz użyć findwzorca, aby znaleźć wszystkie pliki, które nie mają drukowalnych znaków ASCII w swoich nazwach:

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

(Spacja jest pierwszą postacią do wydrukowania wymienioną na stronie http://www.asciitable.com/ , ~jest ostatnią.)

Podpowiedź do LC_ALL=Cjest wymagana (a właściwie LC_CTYPE=Ci LC_COLLATE=C), w przeciwnym razie zakres znaków zostanie nieprawidłowo zinterpretowany. Zobacz także stronę podręcznika glob(7). Ponieważ LC_ALL=Cpowoduje findinterpretację ciągów znaków jako ASCII, wypisze znaki wielobajtowe (np. π) Jako znaki zapytania. Aby to naprawić, podłącz do jakiegoś programu (np. cat) Lub przekieruj do pliku.

Zamiast określać zakresy znaków, [:print:]można również użyć do wybrania „znaków do wydruku”. Pamiętaj, aby ustawić ustawienia regionalne C, w przeciwnym razie otrzymasz dość (pozornie) arbitralne zachowanie.

Przykład:

$ 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
Pamiętaj, że masz nazwy plików, które używają obcych zestawów znaków, które są niezgodne z UTF-8 lub ASCII. W takich przypadkach zamiast znaków możesz zobaczyć znaki zapytania.
Lekensteyn

1
+1, ale użyłbym LC_ALL=Czamiast, LC_COLLATE=Cponieważ nie ma większego sensu ustawianie LC_COLLATE na C bez ustawiania LC_CTYPEi upewnienie się, że nadal działa, nawet gdy zmienna LC_ALL znajduje się w środowisku.
Stéphane Chazelas

Jeśli SPCmożna drukować , to co TABi LFjakie zwykle można znaleźć w plikach tekstowych?
Stéphane Chazelas

1
Dzięki - znaleziono sześć plików, które miały długi myślnik, krótki myślnik i wariant pojedynczego cudzysłowu. Wszystkie pochodzą z MS Word. Bez różnicy w plikach wymienionych między LC_ALL i LC_COLLATE. LC_COLLATE poprawnie wyświetlał znaki spoza ASCII, podczas gdy LC_ALL wyświetlał ??? zamiast. Doskonała odpowiedź!
podejrzany

1
@suspectus Zaktualizowałem przez odpowiedź na podstawie sugestii Stephane. Dla LC_COLLATEi LC_CTYPEpatrz także strona find(1)podręcznika.
Lekensteyn

6

Jeśli przetłumaczysz każdą nazwę pliku za pomocą tr -d '[\200-\377]'i porównasz ją z oryginalną nazwą, wówczas nazwy plików zawierające znaki specjalne nie będą takie same.

(Powyższe przy założeniu, że masz na myśli non-ASCII z zagranicznym)


2
To również usuwa [i ]w większości trimplementacji.
Stéphane Chazelas

Tak - usunąłem [i ]w moim systemie.
podejrzany

+1 - rozwiązanie znalazło wszystkie (sześć) nazw plików bez symboli ASCII (oprócz [and ]). dzięki.
podejrzany

3

Możesz użyć, traby usunąć dowolny obcy znak z nazwy pliku i porównać wynik z oryginalną nazwą pliku, aby sprawdzić, czy zawiera on obce znaki.

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
jest to miłe rozszerzenie mojej odpowiedzi, ale jest zbyt proste, nazwy plików mogą zawierać nowe wiersze, a wtedy twój skrypt nie będzie działał
Timo

1
Jeśli chcesz przetworzyć finddane wyjściowe, użyj wyjścia / danych zakończonych przez NUL, jak pokazano w tej odpowiedzi .
Lekensteyn

0

Przyjęta odpowiedź jest pomocna, ale jeśli twoje nazwy plików są już w kodowaniu określonym w LANG/ LC_CTYPE, lepiej po prostu zrobić:

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

Na klasy znaków ma wpływ LC_CTYPE, ale powyższe polecenie nie używa klas znaków, tylko zakresy, więc LC_CTYPEpo prostu zapobiega zastępowaniu niezwykłych znaków znakami zapytania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.