Bir dosyayı işlemsel olarak nasıl kopyalayabilirim?


9

A'dan B'ye farklı dosya sistemlerinde olabilecek bir dosyayı kopyalamak istiyorum.

Bazı ek gereksinimler vardır:

  1. Kopya tamamen ya da hiçbir şey değil, kısmi ya da bozuk B dosyası çöktü;
  2. Mevcut bir dosyanın üzerine B yazmayın;
  3. Aynı komutun eşzamanlı yürütülmesi ile rekabet etmeyin, en fazla biri başarılı olabilir.

Bence bu yaklaşıyor:

cp A B.part && \
ln B B.part && \
rm B.part

Ancak 3. bpart varsa (-n bayrağıyla bile) cp başarısız olmaz. Daha sonra 1. diğer işlem cp'yi 'kazanırsa' ve yerine bağlanan dosya eksikse başarısız olabilir. B.part da alakasız bir dosya olabilir, ancak bu durumda diğer gizli isimleri denemeden başarısız olurum.

Bence basn noclobber bu işe yarıyor mu? Bash sürümü gereksinimi olmadan almanın bir yolu var mı?

#!/usr/bin/env bash
set -o noclobber
cat A > B.part && \
ln B.part B && \
rm B.part

Takip, biliyorum bazı dosya sistemleri zaten bu (NFS) başarısız. Bu tür dosya sistemlerini tespit etmenin bir yolu var mı?

Benzer ancak aynı olmayan diğer bazı sorular:

Dosya sistemleri arasında atomik hareketin tahmini?

MV atomum fs üzerinde?

eMMC'de dosya ve dizini tempfs'den ext4 bölümüne atomik olarak taşımanın bir yolu var mı?

https://rcrowley.org/2010/01/06/things-unix-can-do-atomically.html


2
Yalnızca aynı komutun eşzamanlı olarak yürütülmesi (yani, aracınızın içinde kilitleme yeterli olabilir) veya dosyalar ile dışarıdaki diğer müdahaleler hakkında mı endişeleniyorsunuz?
Michael Homer

3
"İşlemsel" daha iyi olabilir
muru

1
@MichaelHomer araç içindeki yeterince iyi, dışarıda işleri çok zorlaştıracağını düşünüyorum! Dosya kilitleri ile mümkün olsa ...
Evan Benn

1
@marcelm mvmevcut bir B dosyasının üzerine yazacak ve mv -nbaşarısız olduğunu bildirmeyecektir. ln(1)( rename(2)) B zaten varsa başarısız olur.
Evan Benn

1
@EvanBenn İyi bir nokta! Gereksinimlerinizi daha iyi okumalıydım. (Mevcut bir hedefin atomik güncellemelerine ihtiyacım var ve bunu aklımda
cevaplıyordum

Yanıtlar:


11

rsyncbu işi yapar. O_EXCLVarsayılan olarak geçici bir dosya oluşturulur (yalnızca kullanırsanız devre dışı bırakılır --inplace) ve ardından renamedhedef dosya üzerinde oluşturulur. --ignore-existingVarsa B'nin üzerine yazmak için kullanın .

Uygulamada, ext4, zfs ve hatta NFS montajlarında bununla ilgili herhangi bir sorun yaşamadım.


rsync muhtemelen bunu güzel yapıyor, ama son derece karmaşık bir adam sayfası beni korkutuyor. diğer seçenekleri ima eden, birbiriyle bağdaşmayan seçenekler
Evan Benn

Rsync, söyleyebildiğim kadarıyla # 3 gereksinimine yardımcı olmaz. Yine de, bu harika bir araç ve biraz man-sayfa okumasından kaçınmamalısınız. Ayrıca github.com/tldr-pages/tldr/blob/master/pages/common/rsync.md veya cheat.sh/rsync'yi de deneyebilirsiniz . (tldr ve hile, belirttiğiniz soruna yardımcı olmayı amaçlayan iki farklı projedir, örn., "man sayfası TL; DR"; birçok ortak komut desteklenir ve gösterilen en yaygın kullanımları görürsünüz.
sitaram

@EvanBenn rsync inanılmaz bir araçtır ve öğrenmeye değer! Adam sayfası karmaşık çünkü çok yönlü.
Josh

@sitaram, # 3 bir pid dosyası ile çözülebilir. Buradaki cevapta olduğu gibi küçük bir senaryo .
Robert Riedl

2
Bu en iyi cevap. Rsync, atomik dosya aktarımları için endüstri standardıdır ve çeşitli yapılandırmalarda tüm gereksinimlerinizi karşılayabilir.
wKavey

4

Teşekkürler, bu özlü yanıtı kabul etmek cazip. NFS gibi tehlikeli dosya sistemlerine herhangi bir yorumunuz var mı?
Evan Benn

@EvanBenn, NFS'nin sizi burada bir şekilde karıştırıp karıştırmayacağından emin olmadığımı eklemek istedim, ama unuttum.
ilkkachu

4

NFS'yi sordunuz. Bu tür kodun NFS altında kırılması muhtemeldir, çünkü denetim noclobberiki ayrı NFS işlemi içerdiğinden (dosyanın var olup olmadığını kontrol edin, yeni dosya oluştur) ve iki ayrı NFS istemcisinden iki işlem her ikisinin de başarılı olduğu bir yarış durumuna girebilir ( her ikisi de B.parthenüz var olmadığını doğrular , ardından her ikisi de başarılı bir şekilde oluşturmaya devam eder, sonuç olarak birbirlerinin üzerine yazarlar.)

Yazdığınız dosya sisteminin noclobberatom gibi bir şeyi destekleyip desteklemeyeceği konusunda genel bir denetim yapmanız gerekmez. Dosya sistemi türünü NFS olup olmadığını kontrol edebilirsiniz, ancak sezgisel ve mutlaka bir garanti değildir. SMB / CIFS (Samba) gibi dosya sistemlerinin aynı sorunlardan muzdarip olması muhtemeldir. FUSE yoluyla ortaya çıkan dosya sistemleri doğru davranabilir veya davranmayabilir, ancak bu çoğunlukla uygulamaya bağlıdır.


Muhtemelen daha iyi bir yaklaşım, B.partbenzersiz bir dosya adı kullanarak (diğer ajanlarla işbirliği yoluyla) adımda çarpışmayı önlemek, böylece güvenmenize gerek kalmaz noclobber. Örneğin, dosya adının bir parçası olarak ana bilgisayar adınızı, PID'nizi ve bir zaman damgasını (+ muhtemelen rastgele bir sayı) dahil edebilirsiniz. Belirli bir PID altında belirli bir PID altında herhangi bir zamanda çalışan tek bir işlem olması gerektiğinden, bu benzersizliği garanti eder.

Yani şunlardan biri:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.

Veya:

test -f B && continue  # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
    echo "Success creating B"
else
    echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"

Dolayısıyla, iki ajan arasında bir yarış durumunuz varsa, her ikisi de işleme devam eder, ancak son işlem atomik olacaktır, bu nedenle B, A'nın tam bir kopyasıyla veya B mevcut değildir.

Kopyadan sonra ve mvveya lnoperasyondan önce tekrar kontrol ederek yarışın boyutunu azaltabilirsiniz , ancak hala küçük bir yarış durumu var. Ancak, yarış koşuluna bakılmaksızın, her iki işlemin de A'dan (veya kaynak olarak geçerli bir dosyadan bir kopya) oluşturmaya çalıştığı varsayılarak, B'nin içeriği tutarlı olmalıdır.

İlk durumda mv, bir yarış olduğunda, son işlem kazanan işlemdir, çünkü rename (2) mevcut bir dosyayı atomik olarak değiştirecektir:

Eğer NEWPATH zaten var erişim girişiminde başka süreç hangi anlamı yok ki, bu atomik değiştirilecektir NEWPATH eksik bulacaksınız. [...]

Eğer NEWPATH var ama operasyon nedense başarısız, rename()garanti bir örneğini bırakmak NEWPATH yerinde.

Bu nedenle, o zaman B tüketen işlemlerin bu işlem sırasında farklı sürümlerini (farklı inode) görebilir. Yazarlar sadece aynı içeriği kopyalamaya çalışıyorsa ve okuyucular sadece dosyanın içeriğini tüketiyorsa, bu iyi olabilir, aynı içeriğe sahip dosyalar için farklı kodlar alırlarsa, aynı şekilde mutlu olurlar.

Sabit bir bağlantı kullanan ikinci yaklaşım daha iyi görünüyor , ancak birçok eşzamanlı istemciden NFS'de sıkı bir döngüde hardlinklerle deneyler yapmayı ve başarıyı saymayı hatırlıyorum ve hala iki istemci bir hardlink yayınladığında göründüğü bazı yarış koşulları var gibi görünüyor operasyon aynı anda, aynı hedefle, her ikisi de başarılı görünüyordu. (Bu davranışın belirli NFS sunucusu uygulaması YMMV ile ilgili olması mümkündür.) Her durumda, muhtemelen aynı tür bir yarış durumu, burada ağır olduğu durumlarda aynı dosya için iki ayrı inode elde edebilirsiniz. bu yarış koşullarını tetiklemek için yazarlar arasındaki uyumluluk. Yazarlarınız tutarlıysa (her ikisi de A'dan B'ye kopyalanıyorsa) ve okuyucularınız yalnızca içeriği tüketiyorsa, bu yeterli olabilir.

Sonunda kilitlemeden bahsettin. Maalesef kilitleme en azından NFSv3'te ciddi bir şekilde eksik (NFSv4 hakkında emin değilim, ama ben de iyi değil.) Kilitlemeyi düşünüyorsanız, dağıtılmış kilitleme için farklı protokollere bakmalısınız, muhtemelen bantla gerçek dosya kopyaları, ama bu hem yıkıcı, karmaşık ve çıkmaz gibi sorunlara yatkın, bu yüzden kaçınılması daha iyi olduğunu söyleyebilirim.


NFS'de atomiklik konusunda daha fazla arka plan için, kilitleri önlemek ve NFS'de bile güvenilir bir şekilde çalışmak için oluşturulan Maildir posta kutusu biçimini okumak isteyebilirsiniz . Bunu benzersiz dosya adlarını her yerde tutarak yapar (böylece sonunda nihai bir B bile elde edemezsiniz.)

Belki de sizin durumunuz için biraz daha ilginç olan Maildir ++ formatı Maildir'i posta kutusu kotasına destek eklemek için genişletir ve bunu posta kutusunun içinde sabit bir ada sahip bir dosyayı atomik olarak güncelleyerek yapar (böylece B'nize daha yakın olabilir) NFS'de gerçekten güvenli değil, ancak buna benzer bir prosedür kullanan ve atomik bir değişim olarak geçerli olan bir yeniden hesaplama yaklaşımı var.

Umarım tüm bu işaretçiler faydalı olacaktır!


2

Bunun için bir program yazabilirsiniz.

open(O_CREAT|O_RDWD)Hedef dosyayı açmak için kullanın , hedef dosyanın eksiksiz olup olmadığını kontrol etmek için tüm baytları ve meta verileri okuyun, değilse iki olasılık vardır,

  1. Eksik yazma

  2. Diğer işlemler aynı programı çalıştırıyor.

Hedef dosyada açık bir dosya tanımlama kilidi sağlamaya çalışın.

Başarısızlık, eşzamanlı bir süreç olduğu anlamına gelir, mevcut süreç var olmalıdır.

Başarı, son yazma işleminin çöktüğü anlamına gelir, baştan başlamanız veya dosyaya yazarak düzeltmeye çalışmanız gerekir.

Ayrıca fsync(), dosyayı kapatıp kilidi açmadan önce hedef dosyaya yazdıktan sonra daha iyi olacağınıza dikkat edin , aksi takdirde diğer işlem henüz diskte olmayan verileri okuyabilir.

https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html

Bu, eşzamanlı olarak çalışan bir program ile son olarak çökmüş işlem arasında ayrım yapmanıza yardımcı olmak için önemlidir.


Bilgi için teşekkürler, bunu kendim uygulamak istiyorum ve bir şans vereceğim. Bazı coreutils / benzer paketin bir parçası olarak mevcut olmadığına şaşırdım!
Evan Benn

Bu yaklaşım , kilitlenme gereksiniminde kalan kısmi veya bozuk dosya B'yi karşılayamaz. Dosyayı geçici bir isme kopyalamak, ardından yerine taşımak için standart yaklaşımı kullanmak en iyisidir: hareket atomik olabilir, bu kopyalama olamaz.
reinierpost

@reinierpost Kilitlenme durumunda, ancak veriler tam olarak kopyalanmazsa, ne olursa olsun kısmen kopyalanan veriler kalır. Ama benim yaklaşımım bunu tespit edip düzeltir. Bir dosyayı taşımak atomik olamaz, disk arası fiziksel sektöre yazılan veriler atomik olmayacaktır, ancak yazılım (örn. OS dosya sistemi sürücüsü, bu yaklaşım) dosyayı düzeltebilir (rw ise) veya tutarlı bir durumu (ro ise) bildirebilir , sorunun yorum bölümünde belirtildiği gibi. Ayrıca soru kopyalama değil, hareket etmektir.
德里克 薯条 德里克

Ayrıca, muhtemelen yardımcı olacak O_TMPFILE gördüm. (ve FS'de mevcut değilse hataya neden olmalıdır)
Evan Benn

@Evan belgeyi okudunuz mu veya O_TMPFILE'ın dosya sistemi desteğine neden güveneceğini hiç düşündünüz mü?
德里克 薯条 德里克

0

cpBirlikte yaparak doğru sonucu elde edersiniz mv. Bu, ya "B" yi "A" nın yeni bir kopyasıyla değiştirecek ya da "B" yi olduğu gibi bırakacaktır.

cp A B.tmp && mv B.tmp B

mevcut olanı karşılamak için güncelleme B:

cp A B.tmp && if [ ! -e B ]; then mv B.tmp B; else rm B.tmp; fi

Bu% 100 atomik değil, ama yaklaşıyor. Bu şeylerden ikisinin çalıştığı bir yarış koşulu var, ikisi ifde aynı anda teste giriyor , her ikisi de Bvar olmadığını görüyor , sonra her ikisi de mv.


mv B.tmp B önceden var olan bir B'nin üzerine yazacaktır. cp A B.tmp önceden var olan bir B.tmp'nin üzerine yazacaktır, her iki hata.
Evan Benn

mv B.tmp Bcp A B.tmpilk çalıştırma ve bir başarı sonuç kodu döndürmedikçe çalışmaz . bu nasıl bir başarısızlık? Ayrıca, yapmak istediğiniz şey olan cp A B.tmpmevcut B.tmpolanın üzerine yazacağını kabul ediyorum . &&Ve ilki normalde tamamlar ancak eğer 2 komut çalışacağı garanti eder.
kaan

Soruda başarı önceden varolan dosyanın B üzerine yazılmaması olarak tanımlanır. B.tmp kullanmak bir mekanizmadır, ancak önceden varolan herhangi bir dosyanın üzerine yazılmamalıdır.
Evan Benn

Cevabımı güncelledim. Sonuç olarak, dosyalar mevcut olduğunda veya olmayabilirken tamamen% 100 atomisiteye ve birden fazla iş parçacığına ihtiyacınız varsa, herkesin bir parçası olarak takip ettiği tek bir özel kilit (özel bir dosya oluşturun veya bir veritabanı kullanın veya ...) gerekir. kopyalama / taşıma işlemi.
kaan

Bu güncelleştirme hala B.tmp'nin üzerine yazılıyor ve test ile mv arasında bir yarış durumu var. Evet asıl mesele, umarım yeterince iyi olmayan şeyleri doğru yapmaktır. Diğer cevaplar kilitlerin ve veritabanlarının neden gerekli olmadığını göstermektedir.
Evan Benn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.