Dosyayı yalnızca kapalıysa taşı


10

Harici süreç tarafından oluşturulan büyük dosyayı kapatır kapatmaz taşımak istiyorum.

Bu test komutu doğru mu?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

EDIT: dosya bir veri kümesini temsil eder ve başka bir program üzerinde hareket edebilmesi için taşımak için tamamlanmasını beklemek zorunda. Bu yüzden harici işlemin dosya ile yapılıp yapılmadığını bilmem gerekiyor.


3
Yan not: bir dosya açıldığında, işlemler dosyayı tanımlamak için dosya tanımlayıcılarını ve inode verilerini kullanır. Yolu değiştirmek (yani dosyayı taşımak) işlemde çok fazla sorun yaratmaz.
John WH Smith

2
Harici süreç üzerinde herhangi bir kontrolünüz var mı? Harici işlemin geçici bir dosya oluşturması ve dosyaya yazılması bittiğinde dosyayı yeniden adlandırması mümkün müdür?
Jenny D

@JennyD Bazı araştırmalar yaptım ve doğru olduğu ortaya çıktı. Hiç ihtiyacım yok lsofSadece dosya uzantısının olup olmadığını kontrol etmeliyim .tmp. Bu önemsizdir. Ancak ben hakkında biraz öğrendim beri Sorumu sordum sevindim lsofve inotifyfalan.
Peter Kovac

@PeterKovac Cevapları okuyarak onlar hakkında da daha fazla şey öğrendim, bu yüzden sorduğunuz için çok mutluyum.
Jenny D

@JohnWHSmith - Normalde dosyayı aynı dosya sistemi içinde taşırsanız, dosya yazar yazmayı bitirmeden dosyayı yeni bir dosya sistemine taşırsa, bazı verileri kaybeder.
Johnny

Yanıtlar:


11

Gönderen lsofadam sayfası

Lsof, komut adlarının, dosya adlarının, Internet adreslerinin veya dosyalarının, oturum açma adlarının, NFS dosyalarının, PID'lerin, PGID'lerin veya listelemelerinin istendiği UID'lerin bulunmaması da dahil olmak üzere herhangi bir hata algılanırsa bir (1) döndürür. -V seçeneği belirtilirse, lsof listelenemediği arama öğelerini gösterir.

Bu, lsof failed for some other reasoncümlenizin asla infaz edilmeyeceğini düşündürür .

Harici işleminiz hala açıkken dosyayı taşımayı denediniz mi? Hedef dizin aynı dosya sisteminde ise, altta yatan inode aynı kalacağı için üçüncü bir işlemden orijinal yol altında bu dizine erişmeniz gerekmedikçe bunu yapmakta herhangi bir sorun olmamalıdır. Aksi takdirde mvyine de başarısız olacağını düşünüyorum .

Harici işleminiz dosyayla bitene kadar beklemeniz gerekiyorsa, tekrar tekrar yoklama yerine engelleme yapan bir komut kullanmanız daha iyi olur. Linux'ta inotifywaitbunun için kullanabilirsiniz . Örneğin:

 inotifywait -e close_write /path/to/file

Kullanmanız gerekiyorsa lsof(belki taşınabilirlik için), şöyle bir şey deneyebilirsiniz:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

Güncelleme

Aşağıdaki @JohnWHSmith tarafından belirtildiği gibi, en güvenli tasarım her zaman lsofyukarıdaki gibi bir döngü kullanır , çünkü birden fazla işlemin dosya yazmak için açık olması mümkündür (örnek bir durum, dosyaları okuma ile açan kötü yazılmış bir dizin oluşturma arka plan programı olabilir) / gerçekten okunması gerektiğinde bayrak yaz). inotifywaityine de uyku yerine kullanılabilir, sadece uyku hattını değiştirin inotifywait -e close /path/to/file.


Teşekkürler, farkında değildim inotify. Ne yazık ki, kutumda yüklü değil ama eminim bir yerde bir paket bulacağım. Dosyayı kapatmamın bir nedeni için düzenlememe bakın: Bu bir veri kümesi ve daha fazla işlem yapmadan önce tamamlanması gerekiyor.
Peter Kovac

1
Başka bir not: inotifywaitkomut dosyasının iki kez "yoklamasını" önleyecek olsa da, OP'nin hala lsofbir döngü içinde kontrol etmesi gerekir : dosya iki kez açılırsa inotify, dosya hazır olmayacak olsa bile bir kez kapanmak olayı tetikleyebilir değiştirilir (örneğin, son kod snippet'inizde sleeparamanızla değiştirilebilir inotifywait).
John WH Smith

@John a iyi close_writeolmalı çünkü bir seferde yalnızca bir işlem dosyayı yazmak için açık olabilir. Başka bir kişinin kapatıldıktan hemen sonra açılmayacağını varsayar, ancak daha sonra lsofyoklama ile aynı sorun vardır .
Graeme

1
@Graeme Bu OP'nin durumunda tasarımla doğru olsa da, çekirdek bir dosyanın yazmak için iki kez açılmasına izin verir (bu durumda CLOSE_WRITEiki kez tetiklenir).
John WH Smith

@John, güncellendi.
Graeme

4

Alternatif bir yaklaşım olarak, bu bir boru için mükemmel bir durumdur - ikinci süreç, tüm sürecin bitmesini beklemek yerine, ilk süreçten çıktıyı mümkün olan en kısa sürede işleyecektir:

process1 input_file.dat | process2 > output_file.dat

Avantajları:

  • Genel olarak çok daha hızlı:
    • Diske yazmak ve diskten okumak zorunda değildir (ramdisk kullanıyorsanız bu önlenebilir).
    • Makine kaynaklarını daha eksiksiz kullanmalıdır.
  • Bitirdikten sonra kaldırılacak ara dosya yok.
  • OP'de olduğu gibi karmaşık kilitleme gerekmez.

Doğrudan bir boru oluşturma yönteminiz yoksa, ancak GNU coreutils'iniz varsa bunu kullanabilirsiniz:

tail -F -n +0 input_file.dat | process2 > output_file.dat

Bu , ilk işlemin dosya yazma yoluyla ne kadar ilerlediğine bakılmaksızın (henüz başlamamış ya da zaten bitmiş olsa bile) giriş dosyasını baştan okumaya başlayacaktır .


Evet, bu "açık" çözüm olurdu. Ne yazık ki, veri oluşturma işlemi benim kontrolüm dışında (diğer kullanıcı tarafından çalıştırılır).
Peter Kovac

@PeterKovac Bu alakasız: cat input_file.dat | süreci2 output_file.dat
MariusMatutiae

@MariusMatutiae ancak catve process2daha önce bitiremeden process1bitti. Engellemezlerdi.
cpugeniusmv
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.