Bagaimana saya bisa melakukan operasi "salin jika diubah"?


34

Saya ingin menyalin satu set file dari direktori A ke direktori B, dengan peringatan bahwa jika file dalam direktori A identik dengan file dalam direktori B, file itu tidak boleh disalin (dan dengan demikian waktu modifikasi tidak boleh diperbarui). Apakah ada cara untuk melakukannya dengan alat yang ada, tanpa menulis skrip saya sendiri untuk melakukannya?

Untuk menguraikan sedikit kasus penggunaan saya: Saya membuat autogenerasi banyak .cfile dalam direktori sementara (dengan metode yang harus menghasilkan semuanya tanpa syarat), dan ketika saya membuat ulang, saya ingin menyalin hanya yang telah berubah menjadi direktori sumber aktual, membiarkan yang tidak berubah tidak tersentuh (dengan waktu kreasi lama mereka) sehingga makeakan tahu bahwa itu tidak perlu mengkompilasi ulang mereka. (Namun, tidak semua file yang dihasilkan adalah .cfile, jadi saya perlu melakukan perbandingan biner daripada perbandingan teks.)

(Sebagai catatan: Ini muncul dari pertanyaan yang saya ajukan di https://stackoverflow.com/questions/8981552/speeding-up-file-comparions-with-cmp-on-cygwin/8981762#8981762 , di mana saya mencoba untuk mempercepat file skrip yang saya gunakan untuk melakukan operasi ini, tetapi terpikir oleh saya bahwa saya benar-benar harus bertanya apakah ada cara yang lebih baik untuk melakukan ini daripada menulis skrip saya sendiri - terutama karena cara sederhana melakukan ini dalam shell skrip akan memanggil sesuatu seperti cmppada setiap pasangan file, dan memulai semua proses itu terlalu lama.)


1
Anda dapat menggunakan diff -qr dirA dirBuntuk melihat file apa yang unik dirAdan dirB, secara repektif.

1
@ brooks-moses ini benar-benar pekerjaan yang cocok untuk ccache !
aculich

3
@hesse jika Anda ingin menampilkan file unik yang dapat Anda gunakan diff, tetapi jika Anda ingin melihat apa yang telah berubah maka gunakan rsync -avncatau jauh-jauh rsync --archive --verbose --dry-run --checksum.
aculich

Jawaban:


29

rsync mungkin merupakan alat terbaik untuk ini. Ada banyak opsi pada perintah ini jadi baca halaman manual . Saya pikir Anda menginginkan opsi --checksum atau --ignore-times


Saya seharusnya mencatat bahwa saya sudah mencobanya, tanpa hasil. Kedua opsi tersebut hanya memengaruhi apakah rsync melakukan penyalinan - tetapi, meskipun tidak melakukan penyalinan, rsync akan memperbarui waktu modifikasi file target hingga sama dengan sumbernya (jika -topsi tersebut ditentukan) atau ke waktu sinkronisasi. (jika -ttidak ditentukan).
Brooks Moses

4
@ Brian Musa: Tidak. Setidaknya versi saya rsynctidak. Jika saya melakukan ini :, mkdir src dest; echo a>src/a; rsync -c src/* dest; sleep 5; touch src/a; rsync -c src/* destmaka stat dest/amenunjukkan mtime dan ctime-nya 5 detik lebih tua dari pada src/a.
angus

@angus: Hah. Oke, kamu benar. Kuncinya tampaknya menjadi --checksumpilihan, dan meskipun linux.die.net/man/1/rsync sama sekali tidak mengandung apa pun yang menyiratkan bahwa ia memiliki pengaruh pada apakah tanggal modifikasi diperbarui, namun tetap menyebabkan tanggal modifikasi tujuan dibiarkan. tidak tersentuh. (Di sisi lain, --ignore-timesopsi tidak memiliki efek ini; dengan itu tanggal modifikasi masih diperbarui.) Mengingat bahwa ini tampaknya sepenuhnya tidak terdokumentasi, dapatkah saya mengandalkannya?
Brooks Moses

2
@BrooksMoses: Saya pikir Anda dapat mengandalkan itu: rsyncalur kerja adalah: 1) periksa apakah file perlu diperbarui; 2) jika demikian, perbarui file. The --checksumpilihan mengatakan itu tidak harus diperbarui, sehingga rsynctidak harus melanjutkan ke langkah 2).
enzotib

2
@BrooksMoses: --ignore-timestanpa --checksumakan menyalin setiap file, dan juga memperbarui timestamp, bahkan jika file tersebut identik.
enzotib

13

Anda dapat menggunakan -usakelar untuk cpmenyukainya:

$ cp -u [source] [destination]

Dari halaman manual:

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

4
Hai dan selamat datang di situs ini. Kami berharap jawaban menjadi lebih substansial di sini. Misalnya, Anda bisa menyertakan penjelasan tentang apa yang dilakukan -ubendera dan cara kerjanya dan bagaimana ini akan membantu OP. Namun, dalam kasus khusus ini, itu tidak akan membantu OP karena akan menyalin file yang identik jika mereka lebih baru dan karenanya mengubah perangko waktu mereka yang persis apa yang ingin OP hindari.
terdon

1
Dari komentar pada A serupa yang sudah dihapus: "Ini tidak akan berfungsi karena itu akan menyalin juga file yang identik, jika timestamp sumber lebih baru (dan perbarui timestamp tujuan, terhadap permintaan OP)."
slm

Tidak menjawab pertanyaan sama sekali, tetapi saya masih menemukan itu berguna.
user31389

7

Saat menggunakan rsync --checksumadalah cara umum yang baik untuk "menyalin jika diubah", dalam kasus khusus Anda ada solusi yang lebih baik!

Jika Anda ingin menghindari kompilasi ulang file yang tidak perlu, Anda harus menggunakan ccache yang dibuat untuk tujuan ini! Bahkan, tidak hanya akan menghindari kompilasi ulang yang tidak perlu dari file yang Anda buat secara otomatis, itu juga akan mempercepat segala hal kapan pun Anda lakukan make cleandan kompilasi ulang dari awal.

Selanjutnya saya yakin Anda akan bertanya, "Apakah ini aman?" Ya, seperti yang ditunjukkan situs web:

Apakah ini aman?

Iya nih. Aspek yang paling penting dari cache kompiler adalah selalu menghasilkan output yang sama persis dengan yang dihasilkan oleh kompiler nyata. Ini termasuk menyediakan file objek yang persis sama dan peringatan kompiler yang sama persis yang akan dihasilkan jika Anda menggunakan kompiler nyata. Satu-satunya cara Anda dapat mengetahui bahwa Anda menggunakan ccache adalah kecepatannya.

Dan mudah untuk menggunakannya hanya dengan menambahkannya sebagai awalan di CC=baris makefile Anda (atau Anda dapat menggunakan symlink, tetapi cara makefile mungkin lebih baik).


1
Saya awalnya salah paham dan mengira Anda menyarankan agar saya menggunakan ccache untuk melakukan bagian dari pembuatan, tetapi sekarang saya mengerti - saran Anda adalah bahwa saya cukup menyalin semua file, dan kemudian menggunakan ccache dalam proses pembuatan, sehingga menghindari membangun kembali yang tidak berubah. Ini adalah ide yang bagus, tetapi tidak akan berhasil dalam kasus saya - Saya memiliki ratusan file, biasanya hanya mengubah satu atau dua file sekaligus, dan sedang berjalan di bawah Cygwin di mana cukup memulai ratusan proses ccache untuk melihat masing-masing file akan memakan waktu beberapa menit. Meskipun demikian, terangkat karena itu jawaban yang bagus untuk kebanyakan orang!
Brooks Moses

Tidak, saya tidak menyarankan Anda menyalin semua file, tetapi Anda hanya bisa membuat file .c secara otomatis di tempat (hapus langkah salin dan tulis langsung ke mereka). Dan kemudian gunakan ccache. Saya tidak tahu apa yang Anda maksud dengan memulai ratusan proses ccache ... itu hanya pembungkus ringan di sekitar gcc yang cukup cepat dan akan mempercepat membangun kembali bagian lain dari proyek Anda, juga. Sudahkah Anda mencoba menggunakannya? Saya ingin melihat perbandingan waktu antara menggunakan metode copy Anda dengan ccache. Anda sebenarnya bisa menggabungkan kedua metode untuk mendapatkan manfaat dari keduanya.
aculich

1
Benar, oke, saya mengerti sekarang tentang penyalinannya. Untuk memperjelas, maksud saya adalah ini: Jika saya menghasilkan file di tempat, saya harus memanggil ccache file.c -o file.oatau setara, beberapa ratus kali karena ada beberapa ratus file.cfile. Ketika saya melakukan itu dengan cmp, alih-alih ccache, butuh beberapa menit - dan cmpseringan itu ccache. Masalahnya adalah, pada Cygwin, memulai suatu proses membutuhkan waktu yang tidak dapat diabaikan, bahkan untuk proses yang sepenuhnya sepele.
Brooks Moses

1
Sebagai titik data, for f in src/*; do /bin/true.exe; donebutuh 30 detik, jadi ya. Lagi pula, saya lebih suka editor berbasis Windows saya, dan selain dari masalah waktu semacam ini Cygwin bekerja dengan baik dengan alur kerja saya sebagai tempat paling ringan untuk menguji berbagai hal secara lokal jika saya tidak mengunggah ke server build. Berguna memiliki shell dan editor saya di OS yang sama. :)
Brooks Moses

1
Jika Anda ingin menggunakan editor berbasis Windows Anda, Anda dapat melakukannya dengan mudah dengan Shared Folders jika Anda menginstal Guest Additions ... tapi hei, jika Cygwin cocok untuk Anda, lalu siapa yang dapat saya katakan berbeda? Sepertinya memalukan harus melompat melalui lingkaran aneh seperti ini ... dan kompilasi secara umum akan lebih cepat dalam VM juga.
aculich

3

Ini harus melakukan apa yang Anda butuhkan

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

Dimana:

  • x adalah folder Anda yang diperbarui / baru
  • y adalah tujuan yang ingin Anda salin
  • awk akan mengambil argumen kedua dari setiap baris dari perintah diff (mungkin Anda akan memerlukan beberapa hal tambahan untuk nama file dengan spasi - tidak dapat mencobanya sekarang)
  • xargs -J% akan memasukkan nama file ke cp di tempat yang tepat

1
-1 karena ini terlalu rumit, tidak portabel ( -Jbsd-spesifik; dengan GNU xargs -I), dan tidak berfungsi dengan benar jika set file yang sama tidak ada di kedua lokasi sudah (jika saya touch x/bookemudian grep memberi saya Only in ./x: booyang menyebabkan kesalahan dalam pipa). Gunakan alat yang dibangun untuk pekerjaan itu, seperti rsync --checksum.
aculich

Atau lebih baik lagi, untuk kasus khusus ini gunakan ccache .
aculich

1 karena ini adalah seperangkat perintah terkenal yang dapat saya hentikan untuk digunakan pada tugas yang sama (datang ke sini untuk melakukan diff), masih rsync mungkin lebih baik untuk tugas khusus ini
ntg

3

Saya suka menggunakan serempak yang mendukung rsynckarena mendukung banyak master, setelah mengatur kunci ssh dan vpn saya secara terpisah.

Jadi di crontab saya hanya dengan satu host saya membiarkan mereka melakukan sinkronisasi setiap 15 menit:

* / 15 * * * * [-z "$ (pidof unison)"] && (timeout 25m serentak -sortbysize -ui teks -batch -waktu / home / master ssh: //192.168.1.12//home/master -path dev -logfile /tmp/sync.master.dev.log) &> /tmp/sync.master.dev.log

Lalu saya bisa berkembang di kedua sisi dan perubahan akan menyebar. Bahkan untuk proyek-proyek penting saya memiliki hingga 4 server mirroring pohon yang sama (3 jalankan berbarengan dari cron, menunjuk ke yang tidak). Bahkan, Linux dan Cygwin host dicampur - kecuali jangan berharap keluar dari tautan lunak di win32 di luar lingkungan cygwin.

Jika Anda pergi rute ini, buat cermin awal di sisi kosong tanpa -batch, yaitu

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

Tentu saja ada konfigurasi untuk mengabaikan file cadangan, arsip, dll.:

 ~/.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

Saya melihat itu, tetapi saya tidak dapat menemukan unisonopsi yang berarti "jangan perbarui tanggal file-modifikasi terakhir". Apakah ada satu? Kalau tidak, ini adalah jawaban yang bagus untuk masalah yang sama sekali berbeda.
Brooks Moses

1
-timesmelakukan itu untukku. Unison juga memiliki mode dry-run, pikir saya.
Marcos

Nah, pengaturan times=false(atau meninggalkan -times) akan melakukan itu. Saya tidak tahu bagaimana saya melewatkannya di dokumentasi sebelumnya. Terima kasih!
Brooks Musa

Senang untuk membantu. Saya ngotot dalam hal melestarikan hal-hal seperti modtimes, izin, dan tautan lunak. Seringkali terabaikan
Marcos

1

Sementara rsync --checksum jawaban yang benar, perhatikan bahwa opsi ini tidak kompatibel dengan --times, dan itu --archivetermasuk --times, jadi jika Anda ingin rsync -a --checksum, Anda benar-benar perlu rsync -a --no-times --checksum.


Apa yang Anda maksud dengan mengatakan 'tidak kompatibel'?
Ov

Apa yang Anda maksud dengan "adalah jawaban yang benar"?
thoni56
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.