คุณจะพบปัญหาบางอย่างหากคุณต้องการเปลี่ยนชื่อไฟล์และไดเรกทอรีในเวลาเดียวกัน การเปลี่ยนชื่อไฟล์เป็นเรื่องง่ายพอ แต่คุณต้องการแน่ใจว่าไดเรกทอรีนั้นถูกเปลี่ยนชื่อด้วย คุณทำไม่ได้mv Motörhead/Encöding Motorhead/Encoding
เนื่องจากMotorhead
ไม่มีอยู่ในเวลาที่โทร
ดังนั้นเราต้องมีการสำรวจเส้นทางในระดับลึกของไฟล์และโฟลเดอร์ทั้งหมดจากนั้นเปลี่ยนชื่อไฟล์หรือโฟลเดอร์ปัจจุบันเท่านั้น การทำงานต่อไปนี้กับ GNU find
และ Bash 4.2.42 บน OS X ของฉัน
#!/usr/bin/env bash
find "$1" -depth -print0 | while IFS= read -r -d '' file; do
d="$( dirname "$file" )"
f="$( basename "$file" )"
new="${f//[^a-zA-Z0-9\/\._\-]/}"
if [ "$f" != "$new" ] # if equal, name is already clean, so leave alone
then
if [ -e "$d/$new" ]
then
echo "Notice: \"$new\" and \"$f\" both exist in "$d":"
ls -ld "$d/$new" "$d/$f"
else
echo mv "$file" "$d/$new" # remove "echo" to actually rename things
fi
fi
done
คุณสามารถเปลี่ยน regex โดยใช้new="${f//[\\\/\:\*\?\"<>|]/}"
หากคุณต้องการแทนที่สิ่งที่ Windows ไม่สามารถจัดการได้
บันทึกสคริปต์นี้เป็นให้มันปฏิบัติการด้วยrename.sh
จากนั้นเรียกมันเหมือนchmod +x rename.sh
rename.sh /some/path
ตรวจสอบให้แน่ใจว่าได้แก้ไขการชนชื่อไฟล์ (“ Notice
” ประกาศ)
หากคุณแน่ใจว่าเป็นการเปลี่ยนที่ถูกต้องให้ลบecho
สคริปต์ออกเพื่อเปลี่ยนชื่อสิ่งต่างๆแทนที่จะพิมพ์สิ่งที่ทำ
เพื่อความปลอดภัยฉันขอแนะนำให้ทดสอบสิ่งนี้ในไฟล์ย่อย ๆ ก่อน
ตัวเลือกอธิบาย
เพื่ออธิบายสิ่งที่เกิดขึ้นที่นี่:
-depth
จะตรวจสอบให้แน่ใจว่าไดเรกทอรีถูกเรียกซ้ำแล้วซ้ำก่อนอื่นดังนั้นเราจึงสามารถ "พับ" ทุกสิ่งตั้งแต่ต้นจนจบ โดยปกติแล้วการfind
สำรวจจะแตกต่างกัน (แต่ไม่ใช่ความกว้างก่อน)
-print0
ทำให้แน่ใจว่าfind
เอาต์พุตเป็นตัวคั่นที่ไม่มีค่าดังนั้นเราจึงสามารถอ่านมันread -d ''
เข้าไปในfile
ตัวแปรได้ การทำเช่นนี้ช่วยให้เราจัดการกับชื่อไฟล์แปลก ๆ ทุกประเภทรวมถึงชื่อที่มีช่องว่างและแม้แต่การขึ้นบรรทัดใหม่
dirname
เราจะได้รับไดเรกทอรีของแฟ้มที่มี อย่าลืมพูดตัวแปรของคุณให้ถูกต้องเสมอมิฉะนั้นเส้นทางใดก็ตามที่มีช่องว่างหรืออักขระกลมจะทำให้สคริปต์นี้ผิด
- เราจะได้รับชื่อไฟล์ที่เกิดขึ้นจริง (หรือชื่อ directory)
basename
ด้วย
- จากนั้นเราลบอักขระที่ไม่ถูกต้องออกจากการ
$f
ใช้ความสามารถในการแทนที่สตริงของ Bash ไม่ถูกต้องหมายถึงสิ่งใดก็ตามที่ไม่ใช่ตัวอักษรตัวพิมพ์เล็กหรือใหญ่, ตัวเลข, เครื่องหมายทับ ( \/
), จุด ( \.
), ขีดล่าง, หรือเครื่องหมายขีดกลางลบ
- ถ้า
$f
สะอาดแล้ว (ชื่อที่ทำความสะอาดเหมือนกับชื่อปัจจุบัน) ให้ข้ามไป
- หาก
$new
มีอยู่แล้วในไดเรกทอรี$d
(เช่นคุณมีไฟล์ชื่อresume
และrésumé
อยู่ในไดเรกทอรีเดียวกัน) ออกคำเตือน คุณไม่ต้องการเปลี่ยนชื่อเพราะในบางระบบmv foo foo
จะทำให้เกิดปัญหา มิฉะนั้น,
- ในที่สุดเราก็เปลี่ยนชื่อไฟล์ต้นฉบับ (หรือไดเรกทอรี) เป็นชื่อใหม่
ตั้งแต่นี้จะดำเนินการในลำดับชั้นที่ลึกที่สุด, การเปลี่ยนชื่อMotörhead/Encöding
เพื่อMotorhead/Encoding
ที่จะทำในขั้นตอนที่สอง:
mv Motörhead/Encöding Motörhead/Encoding
mv Motörhead Motorhead
สิ่งนี้ทำให้มั่นใจได้ว่าการเปลี่ยนทั้งหมดจะเสร็จสิ้นในลำดับที่ถูกต้อง
ตัวอย่างไฟล์และทดสอบการทำงาน
สมมติว่าไฟล์บางไฟล์ในโฟลเดอร์ฐานเรียกว่าtest
:
test
test/Motörhead
test/Motörhead/anöther_file.mp3
test/Motörhead/Encöding
test/Randöm
test/Täst
test/Täst/Töst
test/with space
test/with-hyphen.txt
test/work
test/work/resume
test/work/résumé
test/work/schedule
นี่คือเอาต์พุตจากการรันในโหมดดีบัก (ที่echo
ด้านหน้าmv
) เช่นคำสั่งที่จะถูกเรียกและคำเตือนการชนกัน:
mv test/Motörhead/anöther_file.mp3 test/Motörhead/another_file.mp3
mv test/Motörhead/Encöding test/Motörhead/Encoding
mv test/Motörhead test/Motorhead
mv test/Randöm test/Random
mv test/Täst/Töst test/Täst/Tost
mv test/Täst test/Tast
mv test/with space test/withspace
Notice: "resume" and "résumé" both exist in test/work:
-rw-r—r-- … … test/work/resume
-rw-r—r-- … … test/work/résumé
ขอให้สังเกตกรณีที่ไม่มีข้อความสำหรับwith-hyphen.txt
, schedule
และtest
ตัวเอง
mv
มีอยู่แล้วซึ่งสามารถเกิดขึ้นได้ (1) หากคุณมีไฟล์ที่สะอาดอยู่แล้ว (เป็นผลลัพธ์mv foo foo
) หรือ (2) หากคุณมีไฟล์ที่มีชื่อเดียวกันยกเว้น สำหรับอักขระพิเศษ (เช่นmv Encöding Encoding
ที่ซึ่งคุณมีEncoding
ไฟล์เพิ่มเติมอยู่แล้วEncöding
)