Як я можу зробити операцію "копіювати, якщо змінити"?


34

Я хотів би скопіювати набір файлів з каталогу A в каталог B, із застереженням, що якщо файл у каталозі A ідентичний файлу в каталозі B, цей файл не слід копіювати (і, отже, час його модифікації не повинен бути оновлено). Чи є спосіб зробити це за допомогою існуючих інструментів, не пишучи власний сценарій для цього?

Щоб детальніше розібратися в моєму випадку використання: я автоматично генерую купу .cфайлів у тимчасовій папці (методом, який повинен генерувати їх усі беззастережно), і коли я їх знову генерую, я хотів би скопіювати лише ті, що змінилися у фактичний каталог джерел, залишаючи незмінними ті, що не змінюються (зі старим часом створення), щоб makeвони знали, що не потрібно їх перекомпілювати. (Не всі згенеровані файли - це .cфайли, тому мені потрібно робити бінарні порівняння, а не текстові порівняння.)

(Як зауваження: це випливало з питання, яке я задав на https://stackoverflow.com/questions/8981552/speeding-up-file-comparions-with-cmp-on-cygwin/8981762#8981762 , де я намагався щоб прискорити файл сценарію, який я використовував для виконання цієї операції, але мені здається, що я дійсно повинен запитати, чи є кращий спосіб зробити це, ніж писати власний сценарій - тим більше, що будь-який простий спосіб зробити це в оболонці скрипт буде викликати щось на зразок cmpкожної пари файлів, і запуск усіх цих процесів займає занадто багато часу.)


1
Ви можете використовувати, diff -qr dirA dirBщоб побачити, які файли унікальні dirAта dirB, повторно.

1
@ brooks-moses - це справді робота, призначена для кешу !
aculich

3
@hesse, якщо ви хочете показати унікальні файли, ви можете використовувати diff, але якщо ви хочете побачити лише те, що змінилося, використовуйте rsync -avncабо довгий шлях rsync --archive --verbose --dry-run --checksum.
aculich

Відповіді:


29

rsync - це, мабуть, найкращий інструмент для цього. Існує маса варіантів цієї команди, тому читайте сторінку man . Я думаю, що ти хочеш --checksum варіант або --ignore-times


Я мав би зазначити, що я вже пробував це, не маючи успіху. Обидва ці параметри впливають лише на те, чи робить rsync копію, але навіть коли вона не робить копію, вона або оновлює час модифікації цільового файлу на той самий, що і джерело (якщо -tпараметр вказано) або на час синхронізації. (якщо -tне вказано).
Брукс Мойсей

4
@Brooks Мойсей: Це не так. Принаймні, моя версія цього rsyncне робить. Якщо я це роблю:, mkdir src dest; echo a>src/a; rsync -c src/* dest; sleep 5; touch src/a; rsync -c src/* destто stat dest/aпоказує, що його mtime і ctime на 5 секунд старші, ніж значення src/a.
angus

@angus: Так. Гаразд, ти маєш рацію. Ключ, здається, є --checksumваріантом, і хоча linux.die.net/man/1/rsync не містить абсолютно нічого, що означало б, що це може вплинути на оновлення дати модифікації, проте, це призводить до того, що дата зміни призначення буде залишена недоторканий. (З іншого боку, --ignore-timesопція не має цього ефекту; з нею дата модифікації все ще оновлюється.) Враховуючи, що це здається повністю недокументованим, проте чи можу я покластися на нього?
Брукс Мойсей

2
@BrooksMoses: Я думаю, ви можете покластися на нього: rsyncробочий процес: 1) перевірити, чи потрібно оновити файл; 2) якщо так, оновіть файл. --checksumВаріант сказати , що це не повинно оновлюватися, тому rsyncне слід перейти до кроку 2).
enzotib

2
@BrooksMoses: --ignore-timesбез --checksumкопіювання кожного файлу, а також оновлення часової позначки, навіть якщо файли однакові.
enzotib

13

Ви можете використовувати -uкомутатор cpтак, щоб:

$ cp -u [source] [destination]

На чоловіковій сторінці:

   -u, --update
       copy only when the SOURCE file is newer than the destination file or 
       when the destination file is missing

4
Привіт і ласкаво просимо на сайт. Ми очікуємо, що відповіді тут будуть дещо істотнішими. Наприклад, ви могли б включити пояснення того, що -uпрапор робить і як він працює, і як це допоможе ОП. Однак у цьому конкретному випадку це не допоможе ОП, оскільки воно скопіювало б ідентичні файли, якщо вони були б новіші та змінили свої часові позначки, а саме ОП хоче цього уникнути.
terdon

1
З коментаря до аналогічного A, який вже був видалений: "Це не буде працювати, оскільки воно буде копіювати ідентичні файли, якщо часова марка джерела буде новішою (і так оновити часову мітку призначення, на запит ОП)."
slm

Це зовсім не відповідає на питання, але я все-таки вважаю його корисним.
користувач31389

7

Хоча використання rsync --checksum- це хороший загальний спосіб "копіювати, якщо змінити", у вашому конкретному випадку є ще краще рішення!

Якщо ви хочете уникнути зайвої перекомпіляції файлів, ви повинні використовувати ccache, який був створений саме для цієї мети! Насправді, це не тільки дозволить уникнути непотрібних перекомпіляцій автоматично створених файлів, але також прискорить все, коли ви робите це make cleanта повторно компілюєте з нуля.

Далі я впевнений, що ви запитаєте: "Це безпечно?" Ну так, як вказує веб-сайт:

Це безпечно?

Так. Найважливіший аспект кешу компілятора - це завжди створювати точно такий же вихід, який і справжній компілятор. Це включає надання абсолютно однакових файлів об'єктів і точно таких же попереджень компілятора, які були б створені, якщо ви використовуєте реальний компілятор. Єдиний спосіб, коли ви зможете сказати, що ви використовуєте ccache, - це швидкість.

І його легко використовувати , просто додавши його як префікс у CC=рядку вашого makefile (або ви можете використовувати посилання, але спосіб makefile, мабуть, кращий).


1
Я спочатку неправильно зрозумів і подумав, що ти пропонуєш мені використовувати ccache для виконання частини генерування, але тепер я розумію - твоя пропозиція полягала в тому, що я просто копіюю всі файли, а потім використовую ccache в процесі збирання, тим самим уникаючи відновлення тих, які не змінився. Це гарна ідея, але в моєму випадку це не буде добре - у мене сотні файлів, як правило, змінюються лише один-два за один раз, і я працюю під Cygwin, де просто запускаю сотні процесів кешування, щоб переглянути кожен Файл займе кілька хвилин. Тим не менш, цінність, тому що це хороша відповідь для більшості людей!
Брукс Мойсей

Ні, я не пропонував вам копіювати всі файли, скоріше ви можете просто автогенерувати свої файли .c на місці (видаліть крок копіювання та запишіть їх безпосередньо). А потім просто використовувати ccache. Я не знаю, що ви маєте на увазі, запускаючи сотні процесів кеш-пам'яті ... це просто легка обгортка навколо gcc, яка є досить швидкою і також пришвидшить відновлення інших частин вашого проекту. Ви спробували його використовувати? Я хотів би побачити порівняння часу між використанням вашого методу копіювання проти ccache. Насправді ви могли б поєднати два методи, щоб отримати переваги обох.
aculich

1
Так, добре, я зараз розумію про копіювання. Щоб уточнити, що я маю на увазі, це таке: якщо я генерую файли на місці, мені доведеться дзвонити ccache file.c -o file.oабо еквівалент, кілька сотень разів, тому що file.cфайлів є кілька сотень . Коли я робив це з cmp, а не ccache, це зайняло кілька хвилин - і cmpнастільки ж легкий ccache. Проблема полягає в тому, що на Cygwin запуск процесу займає несуттєвий час, навіть для абсолютно тривіального процесу.
Брукс Мойсей

1
Як точка даних, for f in src/*; do /bin/true.exe; doneзаймає 30 секунд, так що так. У будь-якому випадку, я віддаю перевагу моєму редактору Windows, окрім цього випуску часу, Cygwin досить добре працює з моїм робочим процесом як легке місце для тестування речей на місцевому рівні, якщо я не завантажую їх на сервери збирання. Корисно мати мою оболонку та мого редактора в одній ОС. :)
Брукс Мойсей

1
Якщо ви хочете використовувати свій редактор на базі Windows, ви можете зробити це досить легко із спільними папками, якщо встановите додатки для гостей ... але ей, якщо Cygwin вам підходить, то хто я, щоб сказати будь-який інший? Просто ганьба стрибати через такі дивні обручі, як це ... і компіляція взагалі була б швидшою і в VM.
aculich

3

Це має робити те, що вам потрібно

diff -qr ./x ./y | awk '{print $2}' | xargs -n1 -J% cp % ./y/

Де:

  • x - ваша оновлена ​​/ нова папка
  • y - призначення, на яке ви хочете скопіювати
  • awk візьме другий аргумент кожного рядка з команди diff (можливо, вам знадобляться додаткові матеріали для імен файлів з пробілом - не можу зараз спробувати)
  • xargs -J% вставить ім'я файлу cp у потрібне місце

1
-1 тому, що це надмірно складний, не портативний ( -Jспецифічний для bsd; з xargs GNU це -I), і не працює правильно, якщо однаковий набір файлів уже не існує в обох місцях (якщо я touch x/booтоді grep дає мені Only in ./x: booщо викликає помилки в трубопроводі). Використовуйте інструмент, створений для роботи, наприклад rsync --checksum.
aculich

Або ще краще, для цього конкретного випадку використовуйте ccache .
aculich

+1, оскільки його набір добре відомих команд, які я можу перервати, щоб використовувати для подібних завдань (прийшов сюди, щоб зробити різну), все ж rsync може бути кращим для цієї конкретної задачі
ntg

3

Мені подобається використовувати унісон на користь, rsyncтому що він підтримує декілька майстрів, вже налаштувавши мої ключі ssh та vpn окремо.

Тож у моєму Crontab лише одного хоста я дозволяв їм синхронізуватися кожні 15 хвилин:

* / 15 * + dev -logfile /tmp/sync.master.dev.log) &> /tmp/sync.master.dev.log

Тоді я можу розвиватися в будь-яку сторону, і зміни будуть поширюватися. Насправді для важливих проектів у мене є до 4 серверів, які відображають одне і те ж дерево (3 запустіть унісон від cron, вказуючи на той, який не має). Насправді, хости Linux та Cygwin змішані - за винятком випадків, коли не чекайте сенсу від м'яких посилань у програмі win32 поза середовищем cygwin.

Якщо ви йдете цим маршрутом, зробіть початкове дзеркало на порожній стороні без того -batch, тобто

unison -ui text  -times /home/master ssh://192.168.1.12//home/master -path dev

Звичайно, існує конфігурація для ігнорування файлів резервного копіювання, архівів тощо:

 ~/.unison/default.prf :
# Unison preferences file
ignore = Name {,.}*{.sh~}
ignore = Name {,.}*{.rb~}
ignore = Name {,.}*{.bak}
ignore = Name {,.}*{.tmp}
ignore = Name {,.}*{.txt~}
ignore = Name {,.}*{.pl~}
ignore = Name {.unison.}*
ignore = Name {,.}*{.zip}

    # Use this command for displaying diffs
    diff = diff -y -W 79 --suppress-common-lines

    ignore = Name *~
    ignore = Name .*~
    ignore = Path */pilot/backup/Archive_*
    ignore = Name *.o

Я переглянув це, але не зміг знайти unisonваріант, який означає "не оновлювати дати, змінені останніми файлами". Є такий? Інакше це чудова відповідь на зовсім іншу проблему.
Брукс Мойсей

1
-timesробить це для мене. Мені здається, що у "Унісона" також є режим "сухого запуску".
Маркос

Ну, налаштування times=false(або відключення -times) зробить це. Я не знаю, як я пропустив це в документації раніше. Спасибі!
Брукс Мойсей

Радий допомогти. Я є стикером, коли мова йде про збереження таких речей, як часові режими, дозволи та м'які посилання. Часто оглядають
Маркос

1

Незважаючи rsync --checksumна правильну відповідь, зауважте, що ця опція несумісна --times, і вона --archiveвключає --times, тому якщо ви хочете rsync -a --checksum, вам це справді потрібно rsync -a --no-times --checksum.


Що ви маєте на увазі під словом "несумісний"?
OV

Що ви маєте на увазі під "правильною відповіддю"?
thoni56
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.