Ustal, czy plik jest w trakcie zapisywania?

25

Muszę wdrożyć zautomatyzowany proces (za pomocą skryptu cron 1 min), który szuka plików tar w określonym katalogu. Jeśli plik tar zostanie znaleziony, zostanie on rozpakowany do odpowiedniej lokalizacji, a następnie plik tar zostanie usunięty.

Pliki tar są automatycznie kopiowane na ten serwer przez SSH z innego serwera. W niektórych przypadkach pliki tar są bardzo duże i zawierają wiele plików.

Problem, na który mam się natknąć: jeśli skopiowanie pliku tar na serwer zajmie> 1 minutę, a skrypt cron będzie działał co minutę, zobaczy plik .tar.gz i spróbuje to zrobić rozpakuj go, mimo że plik tar nadal jest w trakcie zapisywania.

Czy jest jakiś sposób (za pomocą komend bash) przetestowania, czy plik jest obecnie zapisywany, czy jest to tylko plik częściowy itp.?

Jedną z możliwości, o której myślałem, było skopiowanie pliku jako innego rozszerzenia (jak .tar.gz.part), a następnie zmiana nazwy na .tar.gzpo zakończeniu przesyłania. Ale pomyślałem, że spróbuję dowiedzieć się, czy istnieje prosty sposób, aby najpierw ustalić, czy plik jest cały w wierszu poleceń ... Jakieś wskazówki?

Jake Wilson
źródło
2
Jak dokładnie przesyłany jest plik? Na przykład rsyncużywa tymczasowej nazwy pliku podczas przesyłania (domyślnie) i dopiero po całkowitym przesłaniu pliku zmienia nazwę na rzeczywistą nazwę pliku.
Piskvor

Odpowiedzi:

12

Jesteś na dobrej drodze, zmiana nazwy pliku jest operacją atomową, więc zmiana nazwy po przesłaniu jest prosta, elegancka i nie jest podatna na błędy. Innym podejściem, które mogę wymyślić, jest lsof | grep filename.tar.gzsprawdzenie, czy dostęp do pliku uzyskuje inny proces.

Alex
źródło
7
( lsof filename.tar.gzjest bardziej wydajny i dokładniejszy niż lsof | grep filename.tar.gz)
Rich
BTW, powinna to być absolutna ścieżka do nazwy pliku
DennisLi
14

Najlepszym rozwiązaniem jest lsofustalenie, czy plik został otwarty w wyniku dowolnego procesu:

#  lsof -f -- /var/log/syslog
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
rsyslogd 1520 syslog    1w   REG  252,2    72692 16719 /var/log/syslog

Nie możesz łatwo stwierdzić, czy jest w trakcie pisania, ale jeśli jest zapisywane, MUSI być otwarte.


Edycja: rozwiążmy tutaj rzeczywisty problem, zamiast próbować wdrożyć proponowane rozwiązanie!

Użyj rsync do przesłania pliku:

  rsync -e ssh remote:big.tar.gz .

W ten sposób plik nie zostanie skopiowany ponad istniejący, ale skopiowany do pliku tymczasowego ( .big.tar.gz.XXXXXX) do czasu zakończenia przesyłania, a następnie przeniesiony na miejsce.

MikeyB
źródło
6

Trochę stary, ale większość odpowiedzi całkowicie pomija sens pytania:

Ale pomyślałem, że spróbuję dowiedzieć się, czy istnieje po prostu sposób, aby najpierw ustalić, czy plik jest cały w wierszu poleceń ...

Ogólnie nie ma. Po prostu nie masz wystarczających informacji, aby to ustalić.

Ponieważ ustalenie, że plik jest zamknięty, nie jest tym samym, co ustalenie, czy plik jest cały . Na przykład plik zostanie „zamknięty”, jeśli połączenie zostanie utracone w trakcie przesyłania.

Tylko odpowiedź @ Alexa miała rację. I nawet on zakochał się w lsofjakimś użyciu .

Aby ustalić, czy plik został w pełni przesłany, pomyślne przesłanie wymaga więcej danych. Jak na przykład:

Jedną z możliwości, o której myślałem, było skopiowanie pliku jako innego rozszerzenia (jak .tar.gz.part), a następnie zmiana nazwy na .tar.gzpo zakończeniu przesyłania.

To doskonały sposób na poinformowanie, że plik został w pełni i pomyślnie przesłany. Możesz także przenosić pliki z jednego katalogu do drugiego, o ile pozostajesz w tym samym systemie plików. Lub poproś nadawcę o przesłanie pustego filename.donepliku w celu zasygnalizowania zakończenia.

Ale wszystkie metody muszą polegać na nadawcy, który w jakiś sposób sygnalizuje, że transfer został pomyślnie zakończony. Ponieważ tylko nadawca ma tę informację.

Niektóre formaty plików (takie jak pliki PDF) zawierają dane, które pozwalają ustalić, czy plik jest kompletny. Ale musisz się dowiedzieć i otworzyć prawie cały plik.

lsofpowie ci tylko, że plik nie jest już otwarty - nie powie ci, dlaczego nie jest już otwarty. Nie powie ci też, jak duży powinien być ten plik.

Andrew Henle
źródło
1
Nie mogę tego wystarczająco głosować. Dobra robota w rozwiązywaniu problemu XY tutaj.
Beefster
5

Najlepszym sposobem na to jest użycie incron („system cot inotify”). Pozwala ustawić zegarek inotify w katalogu, który następnie powiadomi cię o operacjach na plikach. W takim przypadku powinieneś obejrzeć katalog w poszukiwaniu close_write. Umożliwi to uruchomienie polecenia po zamknięciu pliku po zapisie.

Kyle
źródło
2

Wygląda na to, że lsof może wykryć, w jakim trybie plik jest otwarty:

lsof -f -- a_file
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     52391 bob    1w   REG    1,2       15 19545007 a_file

Widzisz, gdzie jest napisane 1w? Oznacza to, że numerem deskryptora pliku jest 1, a tryb to w lub zapis.

Kevin Baragona
źródło
Te FDpokazy polowe 3rdla mnie, gdy plik jest otwarty do odczytu.
Sopalajo de Arrierez
0

Używanie inotifywaitmoże osiągnąć to, czego szukasz - może poczekać, aż zapis pliku zakończy się przed wykonaniem polecenia.

Następujące będą stale obserwować folder pod kątem nowych plików i wykonywać polecenie w pętli po zakończeniu zapisu do pliku.

WATCH_DIR=/directory/to/monitor
DEST_DIR=/x/y/z

/usr/bin/inotifywait --recursive --monitor --quiet -e moved_to -e close_write --format '%w%f' "$WATCH_DIR" | while read -r INPUT_FILE; do

mv "$0" "$DEST_DIR"

done

Więcej opcji konfiguracji można znaleźć na stronie https://linux.die.net/man/1/inotifywatch

teeedubb
źródło