Ho eseguito solo test di funzionalità limitati, quindi fai attenzione con questo comando (--dry-run):
rsync -avPr --ignore-existing --remove-source-files src/ dest
Si prega di notare il finale / poiché questo ricorrerà in src invece di copiare src stesso, questo dovrebbe mantenere i percorsi esistenti.
Usando il flag --ignore-existing in combinazione con il flag --remove-source-files eliminerai solo i file da src sincronizzati da src a dest, ovvero i file che non esistevano in precedenza solo in dest.
Per eliminare i file non sincronizzati, cioè quelli che esistevano già in dest/ come in src/, puoi usare:
for file in `find src/ -type f`; do diff $file `echo $file | sed 's/src/dest/'` && rm $file || echo $file; done
o
find src -type f -exec bash -c 'cmp -s "$0" "${0/#src/dest}" && rm "$0"' {} \;
se i nomi dei file possono contenere spazi bianchi/nuove righe/... Per quanto riguarda il commento di Gilles sui caratteri speciali, questo è certamente qualcosa da tenere presente e ci sono molte soluzioni, la più semplice sarebbe quella di passare un -i a rm che richiederà prima di ogni cancellazione. A condizione che src/, o il suo percorso padre, sia fornito per trovare, tuttavia, il percorso completo dovrebbe far sì che tutti i nomi di file vengano gestiti correttamente da entrambi i comandi diff e rm senza virgolette.
unisono è lo strumento che stai cercando. Prova unison-gtk se preferisci una gui. Ma non penso che eliminerà file simili:unison cerca di avere entrambe le directory identiche. Tuttavia 1) identificherà facilmente quali file devono essere copiati; 2) quali richiedono l'unione manuale.
Il seguente script dovrebbe fare le cose in modo ragionevole. Sposta i file dall'origine alla destinazione, senza mai sovrascrivere un file e creando directory se necessario. I file di origine che hanno un file diverso corrispondente nella destinazione vengono lasciati soli, così come i file che non sono file o directory regolari (ad esempio collegamenti simbolici). I file rimasti nella sorgente sono quelli per i quali c'è un conflitto. Attenzione, non l'ho testato affatto.
cd src
find . -exec sh -c '
set -- "/path/to/dest/$0"
if [ -d "$0" ]; then # the source is a directory
if ! [ -e "$1" ]; then
mv -- "$0" "$1" # move whole directory in one go
fi
elif ! [ -e "$0" ]; then # the source doesn't exist after all
: # might happen if a whole directory was moved
elif ! [ -e "$1" ]; then # the destination doesn't exist
mv -- "$0" "$1"
elif [ -f "$1" ] && cmp -s -- "$0" "$1"; then # identical files
rm -- "$0"
fi
' {} \;
Un altro approccio sarebbe quello di fare in modo che un'unione monti una directory sopra l'altra, ad esempio con funionfs o unionfs-fuse.