Linux - копирование только новых и больших файлов

3241
das Keks

У меня есть две директории с тысячами файлов, которые содержат более или менее одинаковые файлы.

Как я могу скопировать все файлы из dirA в dirB, которые не находятся в dirB, или если файл существует в dirB, только перезаписать его, если он меньше.

Я знаю, что есть много примеров для разной отметки времени или разного размера файла, но я хочу перезаписать только, если конечный файл меньше, и ни при каких обстоятельствах, если он больше.

Предыстория моей проблемы:
я отрисовал dynmap на моем сервере Minecraft, но некоторые плитки отсутствуют или повреждены. Затем я снова выполнил рендеринг на другом компьютере с более быстрым ЦП и скопировал все новые отрендеренные файлы (файлы PNG ~ 50 ГБ / 6.000.000 ~ 4-10 КБ) на мой сервер. После этого я заметил, что в моем новом рендере также есть поврежденные файлы.

слева: старый рендер, справа: новый рендер

старый 1 поврежден новый 1

старый 2 новый 2 поврежден

Поэтому я не хочу перезаписывать все файлы, а только те, которые больше (поврежденные несут меньше данных и меньше).

2
Используйте `cp` с комбинацией команд` cmp` или лучше используйте `rsync`, у которого есть все нужные параметры Alex 7 лет назад 0
Какой вариант я должен использовать с rsync? Я не нашел ничего для больших файлов, только более новый или другой размер. Вот почему я спросил. das Keks 7 лет назад 0
Используйте `stat` для файлов в обоих местах, чтобы получить размер файла, а затем скопировать, если он удовлетворяет вашим условиям, то Alex 7 лет назад 0
Ну, это сложная задача, мы искали нужные вам опции rsync, но не смогли найти правильный, поэтому пошли простым путем Alex 7 лет назад 0

3 ответа на вопрос

2
Alex

Может быть, грязный путь, но я надеюсь, что это то, что вы ищете

#!/bin/bash  ### Purpose: # Copy huge amount of files from source to destination directory only if # destination file is smaller in size than in source directory ###  src='./d1' # Source directory dst='./d2' # Destination directory  icp() { f="$"; [ -d "$f" ] && { [ ! -d "$$" ] && mkdir -p "$$"; return }  [ ! -f "$/$" ] && { cp -a "$" "$/$"; return; } fsizeSrc=$( stat -c %s "$f" ) fsizeDst=$( stat -c %s "$/$" ) [ $ -lt $ ] && cp -a "$" "$/$" }  export -f icp export src export dst  find $ -exec bash -c 'icp "$0"' {} \; 
Thanks. I tested it with some test data and it works as I need it. But when I want to execute it on my real data I have a problem because the directory contains too many files (about 6.000.000) :`ls argument list too long`) das Keks 7 лет назад 0
This is operation system limit (you can get it for your system as : `getconf ARG_MAX`). You probably have there pretty long file names or very deep directories structure, so when `find` feed `ls` with such names it exceed maximum allowed length for command line. I modified a little script to eliminate `ls` command, could you try this new version. Alex 7 лет назад 0
If script would choke again, you may try to reduce full path by mounting it to some short path. For example `sudo mkdir -m 777 /a` then mount source directory to `/a` as `sudo mount --bind /pretty/long/prefix/to/source/directory /a` then use `/a` in my script. When you done, unmount `/a` by issue command: `sudo umount /a` Alex 7 лет назад 0
I think it's not the path length since the longest path (including file name) is about 80 characters long. Could it be the list, which is passed to the for each, which is too long? I think this question targets something similar: http://unix.stackexchange.com/questions/128559/solving-mv-argument-list-too-long das Keks 7 лет назад 0
May be `diff --brief -r dir1/ dir2/` is a good approach and then do something for each line of the output. I'll try to construct something like this in the evening. das Keks 7 лет назад 0
@dasKeks You absolutely right, that the list of huge amount of files wont fit in for loop. I rewrote script completely, so it won't choke Alex 7 лет назад 0
Что должен делать параметр `-f`? Я получаю сообщение об ошибке `export: Illegal option -f` das Keks 7 лет назад 0
Это вариант `bash`. Измените `#! / Bin / sh` на` # / bin / bash`, как я сделал в обновленном скрипте Alex 7 лет назад 0
1
Pankaj Jackson

Вы можете использовать команду rsync

Синтаксис:

-a = archive mode -v = increase verbosity -z = compress file data during the transfer --progress = show progress during transfer 

rsync -avz --progress <source path> <destination path>

Вы можете использовать --deleteдля удаления посторонних файлов из каталога назначения

rsync -avz --delete --progress <source path> <destination path>

поэтому ваша команда будет:

rsync -avz --delete --progress dirA dirB 
Разве флаг -a не копирует все файлы с новой отметкой времени или с другим размером? Важно, что только меньшие файлы будут перезаписаны. das Keks 7 лет назад 1
эта команда не будет ничего перезаписывать, она будет копировать только измененный файл и новый файл, который недоступен в Destination Director. Pankaj Jackson 7 лет назад 0
Измененные файлы будут перезаписаны в месте назначения. Независимо от размера файла назначения. Протестировал его с некоторыми данными, и опция -a - это не то, что мне нужно. das Keks 7 лет назад 0
0
user32916

Моя проблема была похожа. Я хотел синхронизировать файлы из удаленной папки в локальную, но копировать только те удаленные файлы, которые были больше, чем соответствующие локальные файлы.

Мой обходной путь с rsync был таким, который на самом деле был однострочным:

for x in $(ls -1 home/me/local/folder/*) do eachsize=$(stat -c "%s") rsync -avz --progress --max-size=$ remote:/home/you/folder/$ . done 

Я думаю, что вы можете понять, так как имена файлов в двух папках одинаковы, я просматриваю каждое из них в локальной папке и сохраняю его размер, а затем устанавливаю его в качестве ограничения, следует ли rsync копировать удаленный файл или нет то же имя, но другой размер.

Не используйте `` ls`` таким образом; просто сделайте `` для х в home / me / local / folder / * ``. G-Man 6 лет назад 0
Вы правы; просто чтобы сделать мою точку зрения, хотя. user32916 6 лет назад 0

Похожие вопросы