Rsyncing files with special characters to USB FAT32

backupfatrsync

I want to copy a large number of files to a USB drive that is formatted FAT32 (and cannot be formatted anything else, unfortunately).

Many of these files have names with characters such as : and ? that FAT32 does not allow. Trying to use cp or rsync, these files are not copied and an error is reported stating so.

I do not want to rename the files at the source, but I also do not care what the files are renamed to on the destination USB drive.

Clarification on the purpose (applies to the other comments as well): This is for an mp3 player in a car and the software only allows FAT32. So it's not for backup purposes. And the reasons the filenames have strange characters is because they are, e.g. titles of mp3s, or names of artists.

I have tried a couple of things:

  1. rsync's --iconv option. This didn't seem to work but perhaps I didn't use it correctly.
  2. rdiff-backup, which I read does this conversion by default. However, the source files are symbolic links that I want followed (i.e. rsync's -L option), and from the manpages it doesn't seem that rdiff-backup has this option.

Any other suggestions?

Best Answer

Rsync with --iconv would be an option, but you'd first need to define an encoding where ?, : and others are encoded with characters that are allowed on FAT filesystems.

For your use case, you aren't using all the power of rsync for this. This task can be done in a few lines of shell script.

Here's a bash script that copies ~/Music to /media/usb99, skipping files that are older on the target, and converts : and ? to _. It doesn't detect clashes (I assume you don't have both foo:bar.mp3 and foo?bar.mp3).

#!/bin/bash
set -e
shopt -s dotglob globstar
cd ~/Music
for source in **/*; 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

This script works under zsh with minor modifications: replace shopt -s dotglob globstar by setopt dot_glob and [[ $target -ot $source ]] by [[ ! -e $target || $target -ot $source ]].

Related Question