Как очистить файл в Perl?
У меня есть Perl-скрипт, который добавляет новую строку в существующий файл каждые 3 секунды. Кроме того, есть приложение C++, которое читает из этого файла.
Проблема в том, что приложение начинает читать файл после того, как скрипт выполнен и дескриптор файла закрыт. Чтобы избежать этого, я хочу очищать после каждого добавления строки, но я новичок в Perl и не знаю, как это сделать.
11 ответов
Пытаться:
use IO::Handle;
$fh->autoflush;
Это было фактически объявлено как способ автоматической промывки в моем раннем вопросе, в котором был задан вопрос о общепринятом плохом способе достижения этого:-)
TL/DR: использовать IO::Handle
и flush
метод, например:
use IO::Handle;
$myfile->flush();
Во-первых, вам нужно решить, насколько "покрасневшим" вы этого хотите. Там может быть довольно много слоев буферизации:
Внутренний буфер Perl для дескриптора файла. Другие программы не могут видеть данные, пока он не покинет этот буфер.
Буферизация на уровне файловой системы "грязных" блоков файлов. Другие программы все еще могут видеть эти изменения, они кажутся "написанными", но они будут потеряны, если ОС или компьютер выйдет из строя.
Буферизация обратной записи на уровне диска. ОС считает, что они записаны на диск, но на самом деле диск просто хранит их в энергозависимой памяти на диске. Если происходит сбой ОС, данные не будут потеряны, но при отключении питания это может произойти, если только диск не сможет записать их первым. Это большая проблема с дешевыми потребительскими твердотельными накопителями.
Это становится еще сложнее, когда подключаются SAN, удаленные файловые системы, RAID-контроллеры и т. Д. Если вы пишете по каналам, есть еще и буфер канала, который необходимо учитывать.
Если вы просто хотите очистить буфер Perl, вы можете close
файл, print
строка, содержащая "\n"
(так как кажется, что Perl сбрасывает символы новой строки), или используйте IO::Handle
"s flush
метод.
Вы также можете использовать perl faq binmode
или играть с $|
сделать дескриптор файла небуферизованным. Это не то же самое, что очистка буферизованного дескриптора, поскольку постановка в очередь группы буферизованных записей и выполнение одного сброса значительно снижает производительность, чем запись в небуферизованный дескриптор.
Если вы хотите очистить буфер обратной записи файловой системы, вам нужно использовать системный вызов, например fsync()
откройте ваш файл в O_DATASYNC
режим, или используйте один из многочисленных других вариантов. Это мучительно сложно, о чем свидетельствует тот факт, что PostgreSQL имеет собственный инструмент для тестирования методов синхронизации файлов.
Если вы хотите убедиться, что он действительно, действительно, честно находится на жестком диске в постоянном хранилище, вы должны сбросить его в файловую систему вашей программы. Вам также необходимо настроить жесткий диск /SSD/RAID-контроллер /SAN/ что угодно, чтобы он действительно сбрасывался, когда ОС запрашивает его. Это может быть на удивление сложным для выполнения и довольно специфичным для ОС / оборудования. Настоятельно рекомендуется провести тестирование по принципу "plug-pull", чтобы убедиться, что вы правильно поняли.
Из 'man perlfaq5':
$old_fh = select(OUTPUT_HANDLE);
$| = 1;
select($old_fh);
Если вы просто хотите сбросить стандартный вывод, вы можете просто сделать:
$| = 1;
Но посмотрите FAQ для деталей о модуле, который дает вам более удобную абстракцию, такую как IO::Handle
,
Вот ответ, реальный ответ.
ОСТАНОВИТЕ поддержание дескриптора открытого файла для этого файла в течение всего процесса.
НАЧАТЬ абстрагирование вашей операции добавления файла в подпрограмму, которая открывает файл в режиме добавления, записывает в него, закрывает его.
#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;
}
процесс, наблюдающий за файлом, столкнется с закрытым файлом, который изменяется при каждом вызове функции.
Все решения, предлагающие установку автоматической очистки, игнорируют тот факт, что большинство современных ОС буферизуют файловый ввод-вывод независимо от того, что делает Perl.
Вы можете принудительно передать данные на диск, только закрыв файл.
Я пойман в ловушку с той же самой дилеммой, где у нас есть проблема с ротацией записываемого журнала.
В PerlDoc есть статья об этом: как очистить / снять буфер с выходного дескриптора файла? Почему я должен это делать?
Два решения:
- Отмените буферизацию обработчика выходных файлов:
$|
- Вызовите метод автоматической очистки, если вы используете
IO::Handle
или один из его подклассов.
Чтобы автоматически очистить вывод, вы можете установить автозапуск /$|
как описано другими, прежде чем выводить в файловый дескриптор.
Если вы уже выполнили вывод в дескриптор файла и хотите убедиться, что он попадает в физический файл, вам нужно использовать IO::Handle flush
а также sync
методы.
Альтернативный подход заключается в использовании именованного канала между вашим Perl-скриптом и программой на C++ вместо файла, который вы используете в данный момент.
Для тех, кто ищет решение построчно сбрасывать выходные данные в файл в Ansys CFD Post с использованием файла сеанса (*.cse), это единственное решение, которое сработало для меня:
! $file="Test.csv";
! open(OUT,"+>>$file");
! select(OUT);$|=1; # This is the important line
! for($i=0;$i<=10;$i++)
! {
! print out "$i\n";
! sleep(3);
! }
Обратите внимание, что вам нужны восклицательные знаки в начале каждой строки, содержащей сценарий Perl.
sleep(3);
применяется только в демонстрационных целях.
use IO::Handle;
не нужен.
Подлинный правильный ответ заключается в использовании:-
$|=1; # Make STDOUT immediate (non-buffered)
и хотя это одна из причин вашей проблемы, другая причина той же проблемы заключается в следующем: «Кроме того, есть приложение C++, которое читает из этого файла».
ЧРЕЗВЫЧАЙНО НЕТРИВИАЛЬНО писать код C++, который может правильно читать из растущего файла, потому что ваша программа на C++ встретит EOF, когда дойдет до конца... (вы не можете читать дальше конца файла без серьезных дополнительных ухищрений) - вам нужно сделать кучу сложных вещей с блокировкой ввода-вывода и флагами, чтобы правильно отслеживать файл таким образом (например, как работает команда linux «tail»).
У меня была та же проблема с той лишь разницей, что я снова и снова писал один и тот же файл с новым контентом. Эта ассоциация "$| = 1" и autoflush работали для меня:
open (MYFILE, '>', '/internet/web-sites/trot/templates/xml_queries/test.xml');
$| = 1; # Before writing!
print MYFILE "$thisCardReadingContentTemplate\n\n";
close (MYFILE);
MYFILE->autoflush(1); # After writing!
Удачи. ЧАС