mengidentifikasi file dengan karakter non-ASCII atau karakter yang tidak dapat dicetak dalam nama file

24

Dalam ukuran direktori 80GB dengan sekitar 700.000 file, ada beberapa nama file dengan karakter non-Inggris dalam nama file. Selain menjelajah daftar file dengan susah payah ada di sana:

  • Cara mudah untuk membuat daftar atau mengidentifikasi nama file ini?
  • Cara untuk menghasilkan karakter bahasa non-Inggris yang dapat dicetak - karakter-karakter yang tidak terdaftar dalam rentang yang dapat dicetak man ascii(sehingga saya dapat menguji apakah file-file ini sedang diidentifikasi)?
tersangka
sumber

Jawaban:

32

Dengan asumsi bahwa "asing" berarti "bukan karakter ASCII", maka Anda dapat menggunakan finddengan pola untuk menemukan semua file yang tidak memiliki karakter ASCII yang dapat dicetak atas namanya:

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

(Spasi adalah karakter yang dapat dicetak pertama yang tercantum di http://www.asciitable.com/ , ~adalah yang terakhir.)

Petunjuk untuk LC_ALL=Cdiperlukan (sebenarnya, LC_CTYPE=Cdan LC_COLLATE=C), jika rentang karakter ditafsirkan salah. Lihat juga halaman manual glob(7). Karena LC_ALL=Csebab finduntuk menafsirkan string sebagai ASCII, itu akan mencetak karakter multi-byte (seperti π) sebagai tanda tanya. Untuk memperbaiki ini, pipa ke beberapa program (misalnya cat) atau redirect ke file.

Alih-alih menentukan rentang karakter, [:print:]juga dapat digunakan untuk memilih "karakter yang dapat dicetak". Pastikan untuk mengatur lokal C atau Anda mendapatkan perilaku yang cukup (tampaknya) sewenang-wenang.

Contoh:

$ 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
sumber
1
Ketahuilah bahwa Anda memiliki nama file yang menggunakan set karakter asing yang tidak kompatibel dengan UTF-8 atau ASCII. Dalam kasus tersebut, Anda dapat melihat tanda tanya alih-alih karakter.
Lekensteyn
1
+1, tetapi saya akan menggunakannya LC_ALL=Csebagai ganti LC_COLLATE=Ckarena tidak masuk akal untuk mengatur LC_COLLATE ke C tanpa pengaturan LC_CTYPEdan untuk memastikan itu masih berfungsi bahkan ketika variabel LC_ALL berada di lingkungan.
Stéphane Chazelas
Jika SPCdapat dicetak , lalu bagaimana TABdan LFyang juga biasanya ditemukan dalam file teks?
Stéphane Chazelas
1
Terima kasih - ini menemukan enam file, yang memiliki tanda hubung panjang, tanda hubung pendek, dan varian kutipan tunggal. Ini semua berasal dari MS Word. Tidak ada perbedaan dalam file yang terdaftar antara LC_ALL dan LC_COLLATE. LC_COLLATE menampilkan karakter non-ASCII dengan benar sedangkan LC_ALL ditampilkan ??? sebagai gantinya. Jawaban yang sangat bagus!
tersangka
1
@suspectus Saya memperbarui dengan jawaban berdasarkan saran dari Stephane. Untuk LC_COLLATEdan LC_CTYPE, lihat juga halaman find(1)manual.
Lekensteyn
6

Jika Anda menerjemahkan setiap nama file menggunakan tr -d '[\200-\377]'dan membandingkannya dengan nama asli, maka setiap nama file yang memiliki karakter khusus tidak akan sama.

(Di atas dengan asumsi bahwa yang Anda maksud non-ASCII dengan asing)

Timo
sumber
2
Itu juga menghapus [dan ]di sebagian besar trimplementasi.
Stéphane Chazelas
Ya - itu menghapus [dan ]di sistem saya.
tersangka
+1 - solusinya memang menemukan semua (enam) nama file dengan simbol non ASCII (selain [dan ]s). Terima kasih.
tersangka
3

Anda dapat menggunakan truntuk menghapus karakter asing apa pun dari nama file dan membandingkan hasilnya dengan nama file asli untuk melihat apakah itu berisi karakter asing.

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
sumber
4
itu adalah ekstensi yang bagus untuk jawaban saya, tetapi terlalu sederhana, nama file dapat memiliki baris baru di dalamnya dan kemudian skrip Anda tidak akan berfungsi
Timo
1
Jika Anda ingin memposting findkeluaran proses , gunakan keluaran / input yang diakhiri NUL seperti yang ditunjukkan dalam jawaban ini .
Lekensteyn
0

Jawaban yang diterima sangat membantu, tetapi jika nama file Anda sudah ada dalam pengkodean yang ditentukan di LANG/ LC_CTYPE, lebih baik lakukan saja:

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

Kelas karakter dipengaruhi oleh LC_CTYPE, tetapi perintah di atas tidak menggunakan kelas karakter, hanya rentang, jadi LC_CTYPEhanya mencegah karakter yang tidak biasa digantikan oleh tanda tanya.

SamB
sumber