rsync tem um sinalizador, --existing , que evitará a cópia de arquivos que ainda não existem no destino:
find . -type f -name '*.txt' -exec rsync --existing {} "$HOME/alltxt/" ';'
Também adicionei -type f para garantir que find encontre apenas arquivos regulares e removi o -R flag ( --relative ) de rsync porque não acho que é útil nesse caso (usar esse sinalizador também é a razão pela qual você obtém os subdiretórios no destino). Eu também estou usando $HOME ao invés de ~ porque eu acho que parece mais legal.
Você também pode adicionar -a ( --archive ) à invocação rsync para preservar os tempos de propriedade e modificação, etc.
Uma abordagem semelhante, mas usando cp em vez de rsync :
find . -type f -name '*.txt' \
-exec sh -c 'for n; do d="$HOME/alltxt/${n##*/}"; [ -f "$d" ] && cp "$n" "$d"; done' sh {} +
Isso encontra todos os arquivos .txt e chama um script de shell curto:
for n; do
d="$HOME/alltxt/${n##*/}"
[ -f "$d" ] && cp "$n" "$d"
done
Esse loop recebe vários arquivos .txt de find . Para cada um desses nomes n , ele cria o caminho do arquivo de destino em d , verifica se o caminho se refere a um arquivo regular existente e, em caso afirmativo, copia o arquivo fornecido para lá.
Em vez do teste [ -f "$d" ] , você pode usar [ "$d" -ot "$n" ] , o que será verdadeiro se o arquivo de destino existir e for mais antigo que o arquivo encontrado. Este é um teste fora do padrão, mas é suportado pela maioria dos shells.
A substituição de variável ${n##*/} removerá os diretórios do caminho em n , de modo que ./some/path/file.txt seja transformado em apenas file.txt (o nome base). Pode ser substituído por $(basename "$n") .