Najprostszym podejściem jest wykorzystanie warstwy systemu plików do przekształcenia nazw plików. Od Ubuntu 12.04 istnieje system plików FUSE, który przekształca nazwy plików w nazwy obsługiwane przez VFAT systemu Windows: fuse-posixovl .
sudo mount.posixovl /media/sdb1
chown guillaume /media/sdb1
rsync -au ~/mail /media/sbd1/
Lub, aby uniknąć konieczności dostępu do konta root:
mkdir ~/mnt
/sbin/mount.posixovl -S /media/sdb1 ~/mnt
rsync -au ~/mail ~/mnt/
Znaki w nazwach plików, których VFAT nie akceptuje, są kodowane tak, jak w %(XX)
przypadku XX
cyfr szesnastkowych. Począwszy od POSIXovl 1.2.20120215, strzeż się, że nazwa pliku taka %(3A)
jest zakodowana jako sama i zostanie zdekodowana jako :
, więc istnieje ryzyko kolizji, jeśli masz nazwy plików zawierające podciągi formularza %(XX)
.
Uwaga: POSIXovl nie radzi sobie z zbyt długimi nazwami plików. Jeśli zakodowana nazwa nie mieści się w 255 znakach, pliku nie można zapisać.
POSIXovl przechowuje uprawnienia uniksowe i własność w plikach o nazwie .pxovl.FILENAME
.
Poniższy skrypt bash ≥4 kopiuje ~/mail/foo:bar
do /media/usb99/mail/foo_bar
i podobnie dla wszystkich plików poniżej ~/mail
. Pliki, które już istnieją w drzewie docelowym i nie są starsze niż źródło, są pomijane.
#!/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
Ten skrypt działa pod Zsh z niewielkimi modyfikacjami: zamień shopt -s dotglob globstar
na setopt dot_glob
i [[ $target -ot $source ]]
przez [[ ! -e $target || $target -ot $source ]]
.
Oto dwuwarstwowy zsh (trzy, jeśli policzysz automatyczne ładowanie). Jest krótszy, ale dość zaawansowany i niezbyt czytelny.
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
Linia jest równoznaczne mkdir -p ~/mail/**/*(…)
z tym, że to nie będzie bombardować, jeśli całkowita długość nazwy katalogów są zbyt długie. Linia ta tworzy katalogi docelowe w razie potrzeby.
~/mail/**/*(/)
rozwija się do wszystkich katalogów w ~/mail
(katalogi tylko ze względu (/)
na koniec).
(/e\''…'\')
wybiera tylko katalogi i dalej wykonuje kod w obrębie „…”, aby przekształcić nazwę każdego pliku, który jest przechowywany w REPLY
zmiennej.
${${REPLY#$HOME/}//:/_}
usuwa prefiks odpowiadający katalogowi źródłowemu i zmienia się :
na _
.
zmv -C
kopiuje każdy plik pasujący do pierwszego operandu (wzorzec Zsh) do nazwy pliku uzyskanej przez rozwinięcie drugiego operandu.
-o -pu
mówi, aby przejść -pu
do cp
narzędzia, aby zachować uprawnienia i kopiować tylko zaktualizowane pliki. (Moglibyśmy powiedzieć zsh, aby sprawdził aktualizację; byłoby to trochę szybsze, ale jeszcze bardziej tajemnicze).
(.)
wybiera tylko zwykłe pliki. -Q
mówi, że należy to przeanalizować jako kwalifikator globalny, a nie jako .
nawiasy wokół niego wskazujące na podwyrażenie.
$1
a $2
w tekście zastępczym pasują do wyrażeń w nawiasach (**/)
i *
. ( **
traci swoje specjalne znaczenie jako zero lub więcej poziomów podkatalogów, jeśli jest w nawiasach, chyba że nawiasy zawierają dokładnie **/
.)
Początkowo myślałem o użyciu pax , który jest narzędziem do archiwizacji (tutaj przeznaczonym do użycia w trybie tranzytowym), który ma funkcję zmiany nazwy pliku (jego -s
opcja). Jednak -s
i -u
opcje nie działają razem (the definition POSIX z pax dosłownie mówi, że -u
musi sprawdzić plik o tej samej nazwie w drzewie docelowym, zamiast nazwy pliku przekształconego przez -s
; realizacja pax w Ubuntu następuje spec dosłownie zamiast pożytecznie). Nadal można go używać do tworzenia twardych linków o zmienionych nazwach, a następnie kopiować twarde linki (za pomocą rsync -au
lub pax -rw -pp -u
) na inne media, ale sprawia to więcej kłopotów niż jest to warte.
cd ~/mail
mkdir -p /media/usb99/mail
pax -rw -l -pp -s '!:!_!g' . ../mail.colonless
rsync -au ../mail.colonless/ /media/usb99/mail/