วิธีที่ตรงไปตรงมาที่สุดคือใช้ประโยชน์จากเลเยอร์ระบบไฟล์เพื่อแปลงชื่อไฟล์ ตั้งแต่ Ubuntu 12.04 มีFUSEระบบแฟ้มที่ชื่อแปลงไฟล์ลงในชื่อว่า Windows ของ VFAT สนับสนุน: ฟิวส์ posixovl
sudo mount.posixovl /media/sdb1
chown guillaume /media/sdb1
rsync -au ~/mail /media/sbd1/
หรือเพื่อหลีกเลี่ยงการต้องการการเข้าถึงรูต:
mkdir ~/mnt
/sbin/mount.posixovl -S /media/sdb1 ~/mnt
rsync -au ~/mail ~/mnt/
อักขระในชื่อไฟล์ที่ VFAT ไม่ยอมรับจะถูกเข้ารหัสเหมือนกับ%(XX)
ที่XX
เป็นเลขฐานสิบหก ในฐานะของ POSIXovl 1.2.20120215 ระวังว่าชื่อไฟล์เหมือน%(3A)
จะถูกเข้ารหัสเป็นตัวเองและจะได้รับการถอดรหัสเป็นจึงมีความเสี่ยงของการปะทะกันถ้าคุณมีชื่อไฟล์ที่มีสตริงของแบบฟอร์ม:
%(XX)
ระวังว่า POSIXovl จะไม่รองรับชื่อไฟล์ที่ยาวเกินไป หากชื่อที่เข้ารหัสไม่พอดี 255 ตัวอักษรไฟล์นั้นจะไม่สามารถจัดเก็บได้
POSIXovl .pxovl.FILENAME
เก็บสิทธิ์ยูนิกซ์และความเป็นเจ้าของในไฟล์ที่เรียกว่า
ทุบตีต่อไป≥4สำเนาสคริปต์~/mail/foo:bar
ไปและในทำนองเดียวกันสำหรับไฟล์ทั้งหมดภายใต้/media/usb99/mail/foo_bar
~/mail
ไฟล์ที่มีอยู่แล้วในแผนผังปลายทางและที่ไม่เก่ากว่าต้นทางจะถูกข้าม
#!/bin/bash
set -e
shopt -s dotglob globstar
for source in "$HOME"/mail/**/*; do
target=/media/usb99/${source#"$HOME"/}
target=${target//:/_}
if [[ -d $source ]]; then
mkdir -p -- "$target"
elif [[ $target -ot $source ]]; then
cp -p -- "$source" "$target"
fi
done
สคริปต์นี้ทำงานภายใต้ zsh มีการปรับเปลี่ยนเล็กน้อย: แทนที่shopt -s dotglob globstar
โดยsetopt dot_glob
และโดย[[ $target -ot $source ]]
[[ ! -e $target || $target -ot $source ]]
นี่คือ zsh สองซับ (สามถ้าคุณนับการแปลอัตโนมัติ) มันสั้นกว่า แต่ค่อนข้างสูงและไม่สามารถอ่านได้มาก
autoload zargs zmv
zargs -- ~/mail/**/*(/e\''REPLY=/media/usb99/${${REPLY#$HOME/}//:/_}'\') -- mkdir -p --
zmv -C -Q -o -pu '~/mail/(**/)(*)(.)' '/media/usb99/mail/${1//:/_}${2//:/_}'
zargs
บรรทัดจะเทียบเท่ากับการmkdir -p ~/mail/**/*(…)
ยกเว้นว่ามันจะไม่ระเบิดออกถ้าความยาวของสะสมของชื่อไดเรกทอรียาวเกินไป บรรทัดนั้นสร้างไดเรกทอรีเป้าหมายตามความจำเป็น
~/mail/**/*(/)
ขยายไปยังไดเรกทอรีทั้งหมดภายใต้~/mail
(ไดเรกทอรีเท่านั้นเนื่องจาก(/)
ในตอนท้าย)
(/e\''…'\')
เลือกเฉพาะไดเรกทอรีและเรียกใช้งานโค้ดเพิ่มเติมภายใน '... ' เพื่อแปลงชื่อไฟล์แต่ละไฟล์ซึ่งเก็บไว้ในREPLY
ตัวแปร
${${REPLY#$HOME/}//:/_}
เอาคำนำหน้าสอดคล้องกับไดเรกทอรีแหล่งที่มาและการเปลี่ยนแปลงเข้าสู่:
_
zmv -C
คัดลอกแต่ละไฟล์ที่ตรงกับตัวถูกดำเนินการแรก (รูปแบบ zsh) ไปยังชื่อไฟล์ที่ได้รับโดยการขยายตัวถูกดำเนินการที่สอง
-o -pu
บอกว่าจะส่งผ่าน-pu
ไปยังcp
ยูทิลิตี้เพื่อรักษาสิทธิ์และคัดลอกเฉพาะไฟล์ที่อัปเดต (เราสามารถบอก zsh ให้ทำการตรวจสอบการอัปเดตมันจะเร็วขึ้นเล็กน้อย แต่เป็นความลับยิ่งขึ้น)
(.)
เลือกไฟล์ปกติเท่านั้น -Q
บอกว่านี่คือการแยกวิเคราะห์เป็นรอบคัดเลือกและไม่เหมือนกับ.
วงเล็บล้อมรอบมันแสดง subexpression
$1
และ$2
ในข้อความเปลี่ยนให้ตรงกับการแสดงออกวงเล็บและ(**/)
*
( **
สูญเสียความหมายพิเศษในระดับศูนย์หรือมากกว่านั้นไดเรกทอรีย่อยหากอยู่ในวงเล็บยกเว้นในวงเล็บมี**/
ทุกประการ)
ตอนแรกฉันคิดว่าจะใช้paxซึ่งเป็นเครื่องมือเก็บถาวร (ที่นี่มีวัตถุประสงค์เพื่อใช้ในโหมด pass-through) ที่มีคุณสมบัติการเปลี่ยนชื่อไฟล์ ( -s
ตัวเลือก) อย่างไรก็ตามตัวเลือก-s
และ-u
ทำงานร่วมกันไม่ได้ ( คำนิยาม POSIX ของ paxบอกว่า-u
ต้องตรวจสอบไฟล์ที่มีชื่อเดียวกันในแผนผังปลายทางมากกว่าที่จะเปลี่ยนชื่อไฟล์โดย-s
การใช้ pax ใน Ubuntu ตามสเป็คมากกว่า ประโยชน์) ยังคงเป็นไปได้ที่จะใช้ประโยชน์จากมันเพื่อสร้างฮาร์ดลิงก์ที่ถูกเปลี่ยนชื่อแล้วคัดลอกฮาร์ดลิงก์ (พร้อมrsync -au
หรือpax -rw -pp -u
) ไปยังสื่ออื่น ๆ แต่มันก็รู้สึกลำบากมากกว่าที่ควรค่า
cd ~/mail
mkdir -p /media/usb99/mail
pax -rw -l -pp -s '!:!_!g' . ../mail.colonless
rsync -au ../mail.colonless/ /media/usb99/mail/