2010-07-10 9 views
5

Piszę trochę Perla, który bierze programy telewizyjne nagrane w Windows Media Center i przenosi/zmienia nazwy/usuwa je w zależności od pewnych kryteriów.Określić, czy plik jest używany w Perlu na Windows

Ponieważ Perl działa dość często, chciałbym czyściutko określić, czy plik jest w użyciu (innymi słowy, program jest w trakcie rejestrowania), dzięki czemu mogę uniknąć robienia czegokolwiek z nim.

Mój obecny sposób patrzy na status pliku (za pomocą „stat”) i porównuje go ponownie po upływie 5 sekund, tak jak poniżej:

sub file_in_use 
{ 
    my $file = shift; 

    my @before = stat($file); 
    sleep 5; 
    my @after = stat($file); 

    return 0 if ($before ~~ $after); 
    return 1; 
} 

wydaje się działać, ale jestem świadomy, że istnieje jest prawdopodobnie lepszym i czystszym sposobem na zrobienie tego.

Czy możesz doradzić?

+0

To jest w porządku approch do niego i Nie wierzę, że znajdziesz wiele opcji w pobliżu ... inny sposób robienia tego będzie polegał na czytaniu pid aplikacji i przenoszeniu plików po pidzie ... – Prix

+1

Od Prix (myślę, że to ważne, żeby wskazać out): * Jeśli plik jest używany w pełnym wymiarze godzin i nie został zwolniony, nie można wykonać przeniesienia [lub usunięcia] pliku, ponieważ spowoduje to błąd ... *. (Kopia może działać w zależności od różnych ustawień trybu plików.) Nawet z podejściem stat, musisz * nadal * obsługiwać scenariusz FS i używać 'file_in_use' tylko jako" podpowiedź "(chociaż potencjalne warunki wyścigu mogą się nigdy nie zmaterializować tutaj). –

+0

@ pst Prawda, dlatego uważam, że przenoszenie to dobra droga ... ponieważ jeśli plik jest nadal zablokowany, to albo zwróci 0, albo powie ci błąd w zależności od tego, jak go kodujesz i możesz próbować do jest wydany lub ... – Prix

Odpowiedz

8

Jeśli nagranie proces blokuje plik, możesz spróbować otworzyć go w trybie odczytu i zapisu i sprawdzić, czy nie zadziała z ERROR_SHARING_VIOLATION jako GetLastError (dostęp przez zmienną specjalną Perla $^E).

Na przykład:

#! /usr/bin/perl 

use warnings; 
use strict; 

sub usage { "Usage: $0 file ..\n" } 

die usage unless @ARGV; 

foreach my $path (@ARGV) { 
    print "$path: "; 

    if (open my $fh, "+<", $path) { 
    print "available\n"; 
    close $fh; 
    } 
    else { 
    print $^E == 0x20 ? "in use by another process\n" : "$!\n"; 
    } 
} 

Przykładowe wyjście z Dir100526Lt.pdf otwartej przez czytnik Adobe:

C:\Users\Greg\Downloads>check-lock.pl Dir100526Lt.pdf setup.exe 
Dir100526Lt.pdf: in use by another process 
setup.exe: available

mieć świadomość, że za każdym razem najpierw sprawdzić stan i potem Działamy w oparciu o wynik ten test, tworzysz race condition. Wydaje się, że najgorsze co może cię ugryźć w aplikacji znajduje się w następującej kolejności pechowy:

  1. Test wideo dostępność jw
  2. odpowiedź: dostępny!
  3. w międzyczasie, rejestrator uruchamia się i zamyka film
  4. z powrotem w swoim programie, spróbuj przenieść film, ale nie jest on z naruszeniem podziału
+0

Idealny! Dziękuję Ci bardzo. Dobra uwaga o stanie wyścigu - mam nadzieję, że po zakończeniu nagrania, nie powinno być żadnego powodu, aby go ponownie uruchomić. – Richard

+0

ciasteczko dla ciebie podoba mi się ta specjalna zmienna ... tylko szybkie pytanie, jeśli zna plik, który musi sprawdzić i sprawdzić, czy jest dostępna w danym momencie i zablokować je od razu, to będzie jak milisekundy, aż inna aplikacja może uzyskać trzymać go tak, że byłoby bardzo bardzo trudno się wydarzyć nie? – Prix

+1

@Prix Milisekunda to długi czas dla komputera. Mało prawdopodobne jest, że * czasami * się zdarzy, a tego rodzaju błędy są trudne do debugowania. To prawda, że ​​w tym przypadku nie jest to warte zamieszania, ale dobrze jest trenować się, aby wykryć pęknięcia synchronizacji. Zamiast sprawdzać dostępność, a następnie blokować, należy natychmiast uzyskać blokadę. Jeśli ktoś go posiada, system przekaże ci go, gdy tylko będzie dostępny. W przeciwnym razie wiesz, że ją masz i nie będzie ona dostępna dla nikogo innego. –

1

Jedynym poprawy proponuję jest stat wszystkich plików na raz, więc trzeba tylko spać przez 5 sekund jeden raz zamiast spania 5 sekund dla każdego pliku:

my (%before, %after); 
foreach my $file (@files_that_might_be_in_use) { 
    $before{$file} = [ stat $file ]; 
} 
sleep 5; 
foreach my $file (@files_that_might_be_in_use) { 
    $after{$file} = [ stat $file ]; 

    if ($before{$file} ~~ $after{$file}) { 
     # file is not in use ... 
    } else { 
     # file is in use ... 
    } 
} 
Powiązane problemy