Самый простой подход - использовать уровень файловой системы для преобразования имен файлов. Начиная с Ubuntu 12.04, существует файловая система FUSE, которая преобразует имена файлов в имена, поддерживаемые VFAT в Windows: fuse-posixovl .
sudo mount.posixovl /media/sdb1
chown guillaume /media/sdb1
rsync -au ~/mail /media/sbd1/
Или чтобы не требовать root-доступа:
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 хранит разрешения и владение Unix в вызываемых файлах .pxovl.FILENAME
.
Следующий скрипт bash ≥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
говорит, что это должно быть проанализировано как глобальный квалификатор, а не как .
с круглыми скобками, указывающими на подвыражение.
$1
и $2
в тексте замены соответствуют выражения в скобках (**/)
и *
. ( **
теряет свое специальное значение как ноль или более уровней подкаталогов, если он указан в скобках, если только скобки не содержат точно **/
.)
Сначала я подумал об использовании pax , который является инструментом архивации (здесь предназначенным для использования в сквозном режиме), который имеет функцию переименования файлов (ее -s
опция). Тем не менее, -s
и -u
варианты не работают вместе ( определение POSIX - во человек буквально говорит , что -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/