web-dev-qa-db-fra.com

Déterminer si le fichier est en cours d'écriture?

J'ai besoin de déployer un processus automatisé (via un script cron de 1 min) qui recherche les fichiers tar dans un répertoire spécifique. Si un fichier tar est trouvé, il n'est pas marqué à l'emplacement approprié, puis le fichier tar est supprimé.

Les fichiers tar sont automatiquement copiés sur ce serveur via SSH à partir d'un autre serveur. Dans certains cas, les fichiers tar sont extrêmement volumineux, avec beaucoup de fichiers.

Le problème que je m'attends à rencontrer: s'il faut> 1 minute pour que le fichier tar soit copié sur le serveur, et que le script cron s'exécute une fois par minute, il va voir le fichier .tar.gz et essayer de le faire décompressez-le, même si le fichier tar est toujours en cours d'écriture.

Existe-t-il un moyen (via les commandes bash) de tester si un fichier est en cours d'écriture, ou s'il ne s'agit que d'un fichier partiel, etc.?

Une alternative à laquelle je pensais était de faire copier le fichier sous une extension de fichier différente (comme .tar.gz.part), puis renommé .tar.gz une fois le transfert terminé. Mais je me suis dit que j'essaierais de déterminer s'il existe simplement un moyen de déterminer si le fichier est entier en premier sur la ligne de commande ... Des indices?

27
Jake Wilson

Vous êtes sur la bonne voie, renommer le fichier est une opération atomique, donc effectuer le changement de nom après le téléchargement est simple, élégant et sans risque d'erreur. Une autre approche à laquelle je peux penser est d'utiliser lsof | grep filename.tar.gz pour vérifier si le fichier est en cours d'accès par un autre processus.

12
Alex

Le mieux est d'utiliser lsof pour déterminer si un fichier a été ouvert par un processus:

#  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

Vous ne pouvez pas facilement dire s'il est en cours d'écriture, mais s'il est en cours d'écriture, il DOIT être ouvert.


Edit: résolvons le problème réel ici plutôt que d'essayer de mettre en œuvre la solution proposée!

Utilisez rsync pour transférer le fichier:

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

De cette façon, le fichier ne sera pas copié par-dessus celui existant mais copié dans un fichier temporaire (.big.tar.gz.XXXXXX) jusqu'à ce que le transfert soit terminé, puis mis en place.

15
MikeyB

Un peu vieux, mais la plupart des réponses manquent complètement le point de la question:

Mais je me suis dit que j'essaierais de déterminer s'il existe simplement un moyen de déterminer si le fichier est entier sur la ligne de commande en premier ...

En général, il n'y en a pas. Vous n'avez tout simplement pas suffisamment d'informations pour le déterminer.

Parce que déterminer que le fichier est fermé n'est pas la même chose que déterminer si le fichier est entier. Par exemple, un fichier sera "fermé" si la connexion est perdue au cours du transfert.

Seule la réponse d'Alex @ a donné raison. Et même il est tombé amoureux de lsof quelque peu.

Pour déterminer si le fichier a été entièrement transféré avec succès, il faut plus de données. Tel que:

Une alternative à laquelle je pensais était de faire copier le fichier sous une extension de fichier différente (comme .tar.gz.part), puis renommé .tar.gz une fois le transfert terminé.

C'est une excellente façon de communiquer que le fichier a été entièrement et correctement transféré. Vous pouvez également déplacer des fichiers d'un répertoire à un autre tant que vous restez dans le même système de fichiers. Ou demandez à l'expéditeur d'envoyer un filename.done fichier pour signaler l'achèvement.

Mais toutes les méthodes doivent s'appuyer sur l'expéditeur pour signaler que le transfert s'est terminé avec succès. Parce que seul l'expéditeur dispose de ces informations.

Certains formats de fichiers (tels que les fichiers PDF) contiennent des données qui vous permettent de déterminer si le fichier est complet. Mais vous devez ouvrir et lire à peu près tout le fichier pour le découvrir.

lsof vous dira simplement que le fichier n'est plus ouvert - il ne vous dira pas pourquoi il n'est plus ouvert. Il ne vous indiquera pas non plus la taille du fichier.

6
Andrew Henle

La meilleure façon de le faire est d'utiliser incron ("inotify cron system"). Il vous permet de définir une veille inotify sur un répertoire qui vous informera ensuite des opérations sur les fichiers. Dans ce cas, vous devez regarder le répertoire pour un close_write. Cela vous permettra ensuite d'exécuter votre commande une fois le fichier fermé après une écriture.

5
Kyle

Il semble que lsof puisse détecter sous quel mode un fichier est ouvert sous:

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

Vous voyez où il est écrit 1w? Cela signifie que le numéro de descripteur de fichier est 1 et que le mode est w, ou écriture.

2
Kevin Baragona

L'utilisation de inotifywait peut réaliser ce que vous recherchez - elle a la capacité d'attendre la fin de l'écriture d'un fichier avant d'exécuter une commande.

Ce qui suit surveillera en continu un dossier pour les nouveaux fichiers et exécutera la commande dans la boucle lorsque l'écriture dans le fichier est terminée.

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

Pour plus d'options de configuration, voir https://linux.die.net/man/1/inotifywatch

0
teeedubb