web-dev-qa-db-fra.com

Comment vider un fichier en Perl?

J'ai un script Perl qui ajoute une nouvelle ligne au fichier existant toutes les 3 secondes. En outre, il existe une application C++ qui lit à partir de ce fichier.

Le problème est que l'application commence à lire le fichier une fois le script terminé et le descripteur de fichier fermé. Pour éviter cela, je veux vider après chaque ajout de ligne, mais je suis nouveau sur Perl et je ne sais pas comment faire.

23
Mihran Hovsepyan

Essayer:

use IO::Handle;
$fh->autoflush;

En fait, cela a été affiché comme un moyen de purge automatique ne de mes premières questions , qui a posé des questions sur la mauvaise façon universellement acceptée d'y parvenir :-)

31
paxdiablo

TL/DR: utilisez IO::Handle Et la méthode flush, par exemple:

use IO::Handle;
$myfile->flush();

Tout d'abord, vous devez décider dans quelle mesure vous voulez "rincer". Il peut y avoir plusieurs couches de mise en mémoire tampon:

  • Tampon interne de Perl sur le descripteur de fichier. D'autres programmes ne peuvent pas voir les données tant qu'elles n'ont pas quitté ce tampon.

  • Mise en mémoire tampon au niveau du système de fichiers des blocs de fichiers "sales". D'autres programmes peuvent toujours voir ces changements, ils semblent "écrits", mais ils seront perdus si le système d'exploitation ou la machine tombe en panne.

  • Tampon d'écriture différée au niveau du disque des écritures. Le système d'exploitation pense que ceux-ci sont écrits sur le disque, mais le disque ne fait que les stocker dans une mémoire volatile sur le lecteur. Si le système d'exploitation se bloque, les données ne seront pas perdues, mais en cas de panne de courant, cela peut être le cas, sauf si le disque peut les écrire en premier. C'est un gros problème avec les SSD grand public bon marché.

Cela devient encore plus compliqué lorsque des SAN, des systèmes de fichiers distants, des contrôleurs RAID, etc. sont impliqués. Si vous écrivez via des tuyaux, il y a aussi le tampon de tuyau à considérer.

Si vous voulez simplement vider le tampon Perl, vous pouvez close le fichier, print une chaîne contenant "\n" (Car il semble que Perl vienne sur les nouvelles lignes), ou - tilisez la méthode flush de IO::Handle .

Vous pouvez également, par la FAQ Perl utiliser binmode ou jouer avec $| Pour rendre le fichier manipulé sans tampon. Ce n'est pas la même chose que de vider une poignée mise en mémoire tampon, car la mise en file d'attente d'un groupe d'écritures mises en mémoire tampon puis faire une seule vidange a un coût de performance beaucoup plus faible que écrit sur une poignée sans tampon.

Si vous souhaitez vider le tampon d'écriture du système de fichiers, vous devez utiliser un appel système comme fsync(), ouvrez votre fichier en mode O_DATASYNC Ou utilisez l'une des nombreuses autres options. C'est douloureusement compliqué, comme en témoigne le fait que PostgreSQL a son propre outil juste pour tester les méthodes de synchronisation des fichiers .

Si vous voulez vous assurer qu'il se trouve vraiment, vraiment et honnêtement sur le disque dur dans le stockage permanent, vous devez le vider dans le système de fichiers de votre programme. Vous devez également configurer le disque dur/SSD/contrôleur RAID/SAN/quoi que ce soit pour vraiment vider lorsque le système d'exploitation le demande. Cela peut être étonnamment compliqué à faire et est assez spécifique au système d'exploitation/matériel. Les tests "plug-pull" sont fortement recommandés pour s'assurer que vous avez vraiment bien compris.

27
Craig Ringer

De "man perlfaq5":

$old_fh = select(OUTPUT_HANDLE);
$| = 1;
select($old_fh);

Si vous voulez simplement vider stdout, vous pouvez probablement simplement faire:

$| = 1;

Mais vérifiez le FAQ pour plus de détails sur un module qui vous donne une abstraction plus agréable à utiliser, comme IO::Handle.

15
Justin Hawkins

Toutes les solutions suggérant de définir un autoflush ignorent le fait de base que la plupart des systèmes d'exploitation modernes tamponnent les E/S de fichiers indépendamment de ce que Perl fait.

Vous ne pouvez forcer l'engagement des données sur le disque qu'en fermant le fichier.

Je suis coincé avec le même dilemme atm où nous avons un problème avec la rotation du journal en cours d'écriture.

2
Rob Wells

Voici la réponse, la vraie réponse.

STOP maintenir un descripteur de fichier ouvert pour ce fichier pendant toute la durée du processus.

START résumant votre opération d'ajout de fichier dans un sous-répertoire qui ouvre le fichier en mode ajout, y écrit, le ferme.

#appends a new line to the existing file
sub append_new_line{
    my $linedata = shift;
    open my $fh, '>>', $fnm or die $!; # $fnm is file-lexical or something
    print $fh $linedata,"\n"; # flavor to taste
    close $fh;
}

le processus observant le fichier rencontrera un fichier fermé qui sera modifié à chaque appel de la fonction.

2
Never Sleep Again

Pour vider automatiquement la sortie, vous pouvez définir le rinçage automatique/$| comme décrit par les autres avant de sortir vers le descripteur de fichier.

Si vous avez déjà sorti dans le descripteur de fichier et devez vous assurer qu'il parvient au fichier physique, vous devez utiliser les méthodes IO :: Handle flush et sync.

1
ysth

Il y a un article à ce sujet dans PerlDoc: Comment puis-je vider/débuffer un descripteur de fichier de sortie? Pourquoi dois-je le faire?

Deux solutions:

  • Unbuffer le gestionnaire de fichiers de sortie avec: $|
  • Appelez la méthode autoflush si vous utilisez IO::Handle ou l'une de ses sous-classes.
1
LaGrandMere

Une approche alternative serait d'utiliser un nommé pipe entre votre script Perl et le programme C++, au lieu du fichier que vous utilisez actuellement.

0
GreenGiant