ระบุไฟล์ที่มีอักขระที่ไม่ใช่ ASCII หรือไม่สามารถพิมพ์ได้ในชื่อไฟล์


24

ในไดเรกทอรีขนาด 80GB ที่มีประมาณ 700,000 ไฟล์มีชื่อไฟล์บางส่วนที่มีอักขระที่ไม่ใช่ภาษาอังกฤษในชื่อไฟล์ นอกเหนือจากการสืบค้นผ่านรายการไฟล์อย่างตั้งใจแล้ว:

  • วิธีง่ายๆในการแสดงรายการหรือระบุชื่อไฟล์เหล่านี้คืออะไร?
  • วิธีสร้างตัวอักษรที่ไม่ใช่ภาษาอังกฤษที่พิมพ์ได้ - ตัวอักษรเหล่านั้นที่ไม่ได้อยู่ในช่วงที่พิมพ์ได้man ascii(ดังนั้นฉันสามารถทดสอบว่าไฟล์เหล่านี้ถูกระบุ)?

คำตอบ:


32

สมมติว่า "foreign" หมายถึง "ไม่ใช่อักขระ ASCII" จากนั้นคุณสามารถใช้findกับรูปแบบเพื่อค้นหาไฟล์ทั้งหมดที่ไม่มีอักขระ ASCII ที่พิมพ์ได้ในชื่อ:

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

(ช่องว่างเป็นอักขระตัวแรกที่พิมพ์ได้ที่อยู่ในรายการhttp://www.asciitable.com/ซึ่ง~เป็นอักขระตัวสุดท้าย)

คำแนะนำสำหรับการLC_ALL=Cเป็นสิ่งจำเป็น (ที่จริงLC_CTYPE=CและLC_COLLATE=C) มิฉะนั้นช่วงตัวอักษรที่ถูกตีความอย่างไม่ถูกต้อง glob(7)ดูเพิ่มเติมหน้าคู่มือ เนื่องจากLC_ALL=Cสาเหตุfindที่แปลสตริงเป็น ASCII มันจะพิมพ์อักขระหลายไบต์ (เช่นπ) เป็นเครื่องหมายคำถาม ในการแก้ไขปัญหานี้ไปป์ไปยังบางโปรแกรม (เช่นcat) หรือเปลี่ยนเส้นทางไปยังไฟล์

แทนที่จะระบุช่วงอักขระคุณ[:print:]สามารถใช้เพื่อเลือก "อักขระที่พิมพ์ได้" อย่าลืมตั้งค่าโลแคล C หรือคุณมีพฤติกรรมตามอำเภอใจ (ดู)

ตัวอย่าง:

$ 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
ระวังว่าคุณมีชื่อไฟล์ที่ใช้ชุดอักขระต่างประเทศที่เข้ากันไม่ได้กับ UTF-8 หรือ ASCII ในกรณีดังกล่าวคุณอาจเห็นเครื่องหมายคำถามแทนตัวอักษร
Lekensteyn

1
+1 แต่ฉันจะใช้LC_ALL=CแทนLC_COLLATE=Cเนื่องจากมันไม่สมเหตุสมผลในการตั้งค่า LC_COLLATE เป็น C โดยไม่ต้องตั้งค่าLC_CTYPEและเพื่อให้แน่ใจว่ายังคงใช้ได้แม้ว่าตัวแปร LC_ALL จะอยู่ในสภาพแวดล้อม
Stéphane Chazelas

หากSPCเป็นพิมพ์แล้วสิ่งที่เกี่ยวกับTABและLFที่นี้ยังมักจะพบในไฟล์ข้อความ?
Stéphane Chazelas

1
ขอบคุณ - มันพบไฟล์หกไฟล์ซึ่งมียัติภังค์ยาวยัติภังค์สั้นและตัวแปรของคำพูดเดี่ยว สิ่งเหล่านี้ล้วนมาจาก MS Word ไม่มีความแตกต่างในไฟล์ที่แสดงระหว่าง LC_ALL และ LC_COLLATE LC_COLLATE แสดงตัวอักษรที่ไม่ใช่ ASCII อย่างถูกต้องในขณะที่ LC_ALL แสดง ??? แทน. คำตอบที่ยอดเยี่ยม!
สงสัย

1
@suspectus ฉันอัปเดตโดยคำตอบตามคำแนะนำจาก Stephane สำหรับLC_COLLATEและLC_CTYPEดูยังfind(1)manpage
Lekensteyn

6

หากคุณแปลชื่อไฟล์แต่ละชื่อโดยใช้tr -d '[\200-\377]'และเปรียบเทียบกับชื่อเดิมชื่อไฟล์ใด ๆ ที่มีอักขระพิเศษจะไม่เหมือนกัน

(ข้างต้นสมมติว่าคุณหมายถึงไม่ใช่ ASCII กับคนต่างชาติ)


2
ที่ยังลบ[และ]ในtrการใช้งานส่วนใหญ่
Stéphane Chazelas

ใช่ - มันลบ[และ]บนระบบของฉัน
สงสัย

+1 - การแก้ปัญหาพบชื่อไฟล์ทั้งหมด (หก) ชื่อที่มีสัญลักษณ์ที่ไม่ใช่ ASCII (นอกเหนือจาก[และ]) ขอบคุณ
สงสัย

3

คุณสามารถใช้trเพื่อลบอักขระต่างประเทศออกจากชื่อไฟล์และเปรียบเทียบผลลัพธ์กับชื่อไฟล์ต้นฉบับเพื่อดูว่ามีอักขระต่างประเทศหรือไม่

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
นั่นเป็นส่วนขยายที่ดีสำหรับคำตอบของฉัน แต่มันง่ายเกินไปชื่อไฟล์อาจมีการขึ้นบรรทัดใหม่จากนั้นสคริปต์ของคุณจะไม่สามารถใช้งานได้
Timo

1
หากคุณต้องการโพสต์โปรเซสfindให้ใช้เอาต์พุต / อินพุตที่ยกเลิก NUL ดังแสดงในคำตอบนี้
Lekensteyn

0

คำตอบที่ได้รับการยอมรับมีประโยชน์ แต่ถ้าชื่อไฟล์ของคุณอยู่ในการเข้ารหัสที่ระบุไว้ในLANG/ LC_CTYPEแล้วควรทำดังนี้:

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

คลาสของตัวละครได้รับผลกระทบจากLC_CTYPEแต่คำสั่งด้านบนไม่ได้ใช้คลาสของตัวอักษรเท่านั้นช่วงดังนั้นLC_CTYPEเพียงป้องกันตัวละครที่ผิดปกติจากการถูกแทนที่ด้วยเครื่องหมายคำถาม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.