syncing from the "file-list" option

Wayne Davison wayned at
Thu Jul 14 03:38:36 GMT 2005

On Wed, Jul 13, 2005 at 07:54:46AM +0000, Monty Ree wrote:
> But if the files are deleted at the remote_server, client doesn't
> delete the file like below and print error. 

That's correct.  There have been some requests for this, but that isn't
how the --files-from list currently works.  You're providing a list of
files to transfer.  Deletions are only ever receiver-side computed based
on the reception of an entire directory's contents.  So, to delete
individual files requires that you either (1) convey the deletions to
the other system via a method outside of rsync:

    cat del-list | ssh remotehost xargs rm
... or (2) that you create a list of parent dirs and exclusions that
fools rsync into deleting what you want.

For example, assume that we want to delete the file baz.txt that used to
be in the directory /var/foo/bar on the sender and is still in the
receiving module's directory "bar".  If we do this:

rsync -av --delete --include=baz.txt --exclude='*' /var/foo/bar/ dest::mod/bar

... rsync would delete that one file and copy nothing.  Extending that
to handle multiple directories in a single call is more complicated, but
can be made simple by using a custom script to create a files-from list
and an exclude-from list based on a list of names to delete.  I've
attached a perl script that I whipped up that does this.

Caution: although I tested the script and it appears to work, be sure
to run the rsync command (the one suggested in the comments) using the
-n option to ensure that rsync is going to do what you expect!

-------------- next part --------------
# To use this script, give it a list of files to be deleted and then
# run rsync with the my-* files it outputs, like this (note the -r!):
# rsync -ar --files-from=my-files --exclude-from=my-excludes --delete /src /dest

use strict;

my(%incl, %excl);

open(EXCLUDES, ">my-excludes") or die $!;
open(FILES, ">my-files") or die $!;

while (<>) {
    my $fn = '';
    my $path = '';
    foreach my $name (split(m#(/)#)) {
	$fn .= $name;
	if ($name eq '/') {
	    $path = $fn;
	    $incl{$path} |= 1;
    $excl{$path} = 1;
    $incl{$_} |= 2;

foreach (sort keys %incl) {
    if (!($incl{$_} & 2)) {
	my($path) = m#(.*/).+#;
	next unless $excl{$path};
    print EXCLUDES "+ $_\n";

foreach (sort keys %excl) {
    print EXCLUDES "- $_*\n";
    my($path) = m#(.*)/#;
    next if $incl{$path} & 2;
    $path = '.' if $path eq '';
    print FILES "$path\n";

close FILES;

More information about the rsync mailing list