A abordagem mais direta é aproveitar a camada do sistema de arquivos para transformar os nomes dos arquivos. Desde o Ubuntu 12.04, existe um sistema de arquivos FUSE que transforma nomes de arquivos em nomes que o VFAT do Windows suporta: fuse-posixovl
.
sudo mount.posixovl /media/sdb1
chown guillaume /media/sdb1
rsync -au ~/mail /media/sbd1/
Ou para evitar exigir acesso root:
mkdir ~/mnt
/sbin/mount.posixovl -S /media/sdb1 ~/mnt
rsync -au ~/mail ~/mnt/
Os caracteres nos nomes de arquivo que o VFAT não aceita são codificados como %(XX)onde XXestão os dígitos hexadecimais. A partir do POSIXovl 1.2.20120215, observe que um nome de arquivo como %(3A)é codificado como ele mesmo e será decodificado como :, para que haja risco de colisão se você tiver nomes de arquivo contendo substrings do formulário %(XX).
Cuidado que o POSIXovl não lida com nomes de arquivos muito longos. Se o nome codificado não couber em 255 caracteres, o arquivo não poderá ser armazenado.
O POSIXovl armazena permissões e propriedade unix em arquivos chamados .pxovl.FILENAME.
O script bash ≥4 a seguir copia ~/mail/foo:barpara /media/usb99/mail/foo_bare da mesma forma para todos os arquivos em ~/mail. Os arquivos que já existem na árvore de destino e que não são mais antigos que a origem são ignorados.
#!/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
Este script funciona no zsh com pequenas modificações: substitua shopt -s dotglob globstarpor setopt dot_globe [[ $target -ot $source ]]por [[ ! -e $target || $target -ot $source ]].
Aqui está um zsh de duas linhas (três se você contar os carregamentos automáticos). É mais curto, mas bastante avançado e não muito legível.
autoload zargs zmv
zargs -- ~/mail/**/*(/e\''REPLY=/media/usb99/${${REPLY#$HOME/}//:/_}'\') -- mkdir -p --
zmv -C -Q -o -pu '~/mail/(**/)(*)(.)' '/media/usb99/mail/${1//:/_}${2//:/_}'
- A
zargslinha é equivalente a mkdir -p ~/mail/**/*(…), exceto que ela não será bombardeada se o comprimento acumulado dos nomes de diretório for muito longo. Essa linha cria os diretórios de destino conforme necessário.
~/mail/**/*(/)expande para todos os diretórios em ~/mail(diretórios somente devido ao (/)final).
(/e\''…'\')seleciona apenas diretórios e executa ainda mais o código em '…' para transformar cada nome de arquivo, que é armazenado na REPLYvariável.
${${REPLY#$HOME/}//:/_}remove o prefixo correspondente ao diretório de origem e muda :para _.
zmv -C copia cada arquivo que corresponde ao seu primeiro operando (um padrão zsh) para o nome do arquivo obtido expandindo seu segundo operando.
-o -pudiz para passar -pupara o cputilitário, a fim de preservar permissões e copiar apenas arquivos atualizados. (Poderíamos dizer ao zsh para executar a verificação da atualização; seria um pouco mais rápido, mas ainda mais enigmático.)
(.)seleciona apenas arquivos regulares. -Qdiz que isso deve ser analisado como um qualificador de glob e não como um .entre parênteses indicando uma subexpressão.
$1e $2no texto de substituição correspondem às expressões entre parênteses (**/)e *. ( **perde seu significado especial como zero ou mais níveis de subdiretório, se estiver entre parênteses, a menos que os parênteses contenham exatamente **/.)
Inicialmente, pensei em usar o pax , que é uma ferramenta de arquivamento (aqui destinada a ser usada no modo de passagem) que possui um recurso de renomeação de arquivos (sua -sopção). No entanto, os -se -uopções não funcionam juntos (a definição POSIX de pax literalmente diz que -udeve verificar um arquivo com o mesmo nome na árvore de destino, em vez do nome do arquivo transformado por -s; a implementação pax no Ubuntu segue a especificação literalmente ao invés de de forma útil). Ainda é possível utilizá-lo para criar links físicos renomeados e depois copiá-los (com rsync -auou pax -rw -pp -u) para outras mídias, mas parece haver mais problemas do que vale a pena.
cd ~/mail
mkdir -p /media/usb99/mail
pax -rw -l -pp -s '!:!_!g' . ../mail.colonless
rsync -au ../mail.colonless/ /media/usb99/mail/