Rsync --delete --files-from = list/dest/не удаляет ненужные файлы
Как видно из названия, я пытаюсь синхронизировать папку со списком файлов. Я надеялся, что эта команда удалит все файлы в dest/, которых нет в списке, но этого не произошло.
Поэтому я немного искал и теперь знаю, что rsync не может этого сделать.
Но мне это нужно, так вы знаете, как это сделать?
PS: список создается скриптом Python, поэтому можно представить, что ваше решение использует некоторый код Python.
РЕДАКТИРОВАТЬ, пусть будет конкретным:
Список выглядит так:
/home/max/Musik/Coldplay/Parachutes/Trouble.mp3
/home/max/Musik/Coldplay/Parachutes/Yellow.mp3
/home/max/Musik/Coldplay/A Rush of Blood to the Head/Warning Sign.mp3
/home/max/Musik/Coldplay/A Rush of B-Sides to Your Head/Help Is Around the Corner.mp3
/home/max/Musik/Coldplay/B-Sides (disc 3)/Bigger Stronger.mp3
и команда, как это:
rsync --delete --files-from=/tmp/list / /home/max/Desktop/foobar/
Это работает, но если я удаляю строку, она не удаляется в foobar/.
РЕДАКТИРОВАТЬ 2:
rsync -r --include-from=/tmp/list --exclude=* --delete-excluded / /home/max/Desktop/foobar/
Это тоже не работает...
Ответы
Ответ 1
Возможно, вы могли бы сделать это, используя вместо этого список включенных шаблонов, и использовать --delete-excluded
(что делает, как следует из названия)? Что-то вроде:
rsync -r --include-from=<patternlistfile> --exclude=* --delete-excluded / dest/
Если имена файлов, вероятно, содержат подстановочные знаки (*
, ?
и [
), вам может потребоваться изменить Python, чтобы избежать их:
re.sub("([[*?])", r"\\\1", "abc[def*ghi?klm")
Изменить: Согласование на основе шаблонов несколько отличается от --files-from
тем, что rsync
не будет возвращаться в каталоги, соответствующие шаблону исключения, по соображениям эффективности. Поэтому, если ваши файлы находятся в /some/dir
и /some/other/dir
, тогда ваш файл шаблонов должен выглядеть так:
/some/
/some/dir/
/some/dir/file1
/some/dir/file2
/some/other/
/some/other/dir/
/some/other/dir/file3
...
В качестве альтернативы, если все файлы находятся в одном каталоге, вы можете немного переписать команду:
rsync -r --include-from=<patternlistfile> --exclude=* --delete-excluded /some/dir/ dest/
а затем ваши шаблоны станут:
/file1
/file2
Изменить: Думая об этом, вы можете включить все каталоги с одним шаблоном:
/**/
но затем вы получите полное дерево каталогов в dest/
, которое, вероятно, не то, что вы хотите. Но объединение этого параметра с -m
(которое создает пустые каталоги) должно решить это - так что команда заканчивается примерно так:
rsync -m -r --delete-excluded --include-from=<patternfile> --exclude=* / dest/
и файл шаблона:
/**/
/some/dir/file1
/some/other/dir/file3
Ответ 2
Это не совсем то решение, но люди, пришедшие сюда, могут найти это полезным: поскольку rsync 3.1.0 имеет параметр --delete-missing-args
, который удаляет файлы в каталоге назначения при синхронизации двух каталогов с помощью --files-from
. Вам нужно будет указать удаленные файлы в /tmp/list
вместе с файлами, которые вы хотите скопировать:
rsync --delete-missing-args --files-from=/tmp/list /source/dir /destination/dir
Подробнее см. справочную страницу.
Ответ 3
Как вы объяснили, команда
rsync -r --delete --files-from=$FILELIST [email protected]:/ $DEST/
не удаляет контент в получателе, когда была удалена запись из $FILELIST. Простое решение заключается в следующем:
mkdir -p $DEST
rm -rf $TEMP
rsync -r --link-dest=$DEST --files-from=$FILELIST [email protected]:/ $TEMP/
rm -r $DEST
mv $TEMP $DEST
Это указывает rsync использовать пустую адресату. Файлы, которые уже присутствуют в каталоге link-dest, локально жестко связаны и не копируются. Наконец, старый пункт назначения заменяется новым. Первый mkdir создает пустой $DEST, если $DEST не существует, чтобы предотвратить ошибку rsync. (Предполагается, что $-variables несут полный путь к соответствующему файлу или каталогу.)
Есть некоторые незначительные накладные расходы для жесткой привязки, но вам не нужно возиться со сложными include/exclude-strategy.
Ответ 4
Вдохновленный из m4t, но используя... rsync для очистки
rsync -r --link-dest=$dst --files-from=filelist.txt [email protected]:$source/ $temp
rsync -ra --delete --link-dest=$temp $temp/ $dest
Ответ 5
Явная сборка --exclude-from =... кажется единственным способом синхронизации списка файлов.
stdin = subprocess.PIPE
other_params.append("--exclude-from=-") #from stdin
p = subprocess.Popen( 'rsync -e ssh -zthvcr --compress-level=9 --delete'.split() + other_params + [src, dst], stdin = PIPE)
if relative_files_list != None:
#hack: listing of excluded files seems the only way to delete unwanted files at destination
files = set(map(norm_fn, relative_files_list)) #make hash table, for huge lists
for path, ds, fs in os.walk(src):
for f in fs:
rel_path_f = norm_fn(os.path.relpath(os.path.join(path, f), src))
if rel_path_f not in files:
#print 'excluding', rel_path_f.replace('\\', '/')
p.stdin.write(rel_path_f + '\n')
p.stdin.close()
assert 0 == p.wait()
Ответ 6
Я понимаю, что этот вопрос был задан давно, но я не был удовлетворен ответом.
Вот как я решил проблему, принимая плейлист, созданный mpd
:
#!/bin/bash
playlist_path="/home/cpbills/.config/mpd/playlists"
playlist="${playlist_path}/${1}.m3u"
music_src="/home/cpbills/files/music"
music_dst="/mnt/sdcard/music/"
if [[ -e "$playlist" ]]; then
# Remove old files
find "$music_dst" -type f | while read file; do
name="$(echo "$file" | sed -e "s!^$music_dst!!")"
if ! grep -qF "$name" "$playlist"; then
rm "$file"
fi
done
# Remove empty directories
find "$music_dst" -type d -exec rmdir {} \; 2>/dev/null
rsync -vu \
--inplace \
--files-from="$playlist" \
"$music_src" "$music_dst"
else
printf "%s does not exist\n" "$playlist" 1>&2
exit 1
fi
Ответ 7
rsync идеально подходит для хранения каталогов в синхронизации, среди других полезных вещей. Если у вас есть точная копия на SOURCE и вы хотите удалить файлы в DEST, вы можете удалить их из SOURCE, а опция rsync --delete
также удалит их из DEST.
Однако, если у вас есть только произвольный список файлов, которые вы хотите удалить, я предлагаю вам использовать SSH для этого:
ssh [email protected] rm /path/to/file1 /path/to/file2
Это приведет к выполнению команды rm
на удаленном хосте.
Используя python, вы можете:
import subprocess
FileList = ['/path/to/file1', '/path/to/file2']
subprocess.call(['ssh', '[email protected]', 'rm'] + FileList)
~ наслаждаться