Можно ли сохранить в дескриптор файла DATA? - PullRequest
3 голосов
/ 05 февраля 2010

Мне было любопытно, если бы использование Storable store_fd и fd_retrieve позволило бы мне сохранить структуру данных в собственном дескрипторе файла DATA программы. Я понимаю, что это не лучшая практика, мне просто любопытно, сработает ли это, мои быстрые попытки попробовать это не сработают.

Ответы [ 4 ]

2 голосов
/ 06 февраля 2010

Я не уверен, почему вы хотите это сделать, но вы можете подделать это.Однако вам следует избегать этого.

Только для хихиканья вы можете открыть дескриптор файла, прочитать строки из $0 и распечатать их, пока не дойдете до __DATA__, а затем добавить новый раздел __DATA__.Хитрость заключается в том, чтобы переименовать новый файл, чтобы он стал $0, возможно, на exec, если ваша система блокирует файл во время работы программы:

#!perl

my $mode = (stat($0))[2] & 07777;

open my($fh), '<', $0 or die "I can't open me! $!\n";
open my($new), '>', "$0.new" or die "I can't open you! $!\n";
eval { chmod( $mode, $new ) } or warn "Couldn't set permissions: $@\n";

while( <$fh> )
    {
    last if /^__DATA__$/;
    print { $new } $_;
    }

print "I am $$\n";
print { $new } "__DATA__\n", join '|', $$, time, (stat($0))[1];

rename( "$0.new", $0 )

__DATA__
64574|1265415126|8843292
1 голос
/ 06 февраля 2010

В обычных условиях это невозможно:

$ cat write-data
<code>#! /usr/bin/perl

use warnings;
print DATA "bar!\n";</code>

$ ./write-data
Name "main::DATA" used only once: possible typo at ./write-data line 6.
print() on unopened filehandle DATA at ./write-data line 6.

Но вы можете создать необычные условия:

#! /usr/bin/perl

use warnings;
use strict;

use Data::Dumper;
use File::Temp qw/ tempfile /;
use Storable qw/ store_fd fd_retrieve /;

sub store_in_DATA {
  my($data) = @_;

  my($fh,$path) = tempfile;
  unlink $path           or warn "$0: unlink: $!";

  *DATA = $fh;
  store_fd $data, \*DATA or warn "$0: print: $!";

  seek DATA, 0, 0        or warn "$0: seek: $!";
}

store_in_DATA { foo => "There is no spoon.\n" };

undef $/;
my $ref = fd_retrieve \*DATA;
print Dumper $ref;

В Windows вы получите предупреждение о unlink из-за его семантики совместного использования файлов по умолчанию. Если это ваша платформа, вы можете выполнить очистку в END раз или использовать Win32 :: SharedFileOpen .

1 голос
/ 06 февраля 2010

DATA - это дескриптор чтения данных, сохраненных в скрипте. Inline :: Files Конвея - единственный из известных мне модулей, который говорит о записываемых виртуальных файлах. А поскольку файлы сценариев обычно являются ASCII, я не знаю, что произойдет, если вы получите двоичный 26-байт в MSDOS или двоичный 4 в UNIX в выводе Storable.

Однако, если вы говорите о вы сохраняете данные путем , набираете их там, только для читаете это из скрипта, тогда бинарная проблема все еще противостоит вам.

Итак, лучше использовать YAML или JSON для настойчивости. Я знаю, что YAML будет обрабатывать благословение при получении данных из данных.

0 голосов
/ 06 февраля 2016

Я придумал свое собственное решение ... не то, чтобы я специально рекомендовал его или любые другие; в моем случае это скрипт модульного теста со ссылочными значениями в многомерной хеш-структуре. Я не буду вдаваться в технические подробности того, что эти вещи делают и почему, но конечный результат заключается в том, что небольшое исправление или изменение в коде может привести ко многим значениям, требующим обновления (после подтверждения изменения действительно). *

Поэтому я переместил хеш в секцию __DATA__, используя Data::Dumper. Код для записи этого в дескриптор файла выглядит так:

use Data::Dumper;
$Data::Dumper::Terse = 1; # to Eval whole thing as a hash
$Data::Dumper::Indent = 1; # Looks better, just a preference
$Data::Dumper::Sortkeys = 1; # To keep changes minimal in source control
print $fh Dumper(\%HASH);

В начале скрипта я загружаю хеш из DATA после сохранения ссылок на позицию исходного дескриптора и mtime (mtime используется для гарантии того, что файл не был изменен во время выполнения скрипта).

use vars qw(%HASH $FILEPOS $MTIME);

{
    $FILEPOS = tell(DATA);
    $MTIME = (stat(DATA))[9];
    local $/;
    my $data = <DATA>;
    %HASH = %{eval $data};
}

Наконец, чтобы обновить раздел __DATA__, я открываю __FILE__ в $FILEPOS, обрезаю его и пишу в него. Я упростил обработку ошибок для этого примера.

open(my $fh, '>>', __FILE__) or die $!;
seek($fh, $FILEPOS, 0) or die $!;
die "File changed" if ((stat($fh))[9] != $MTIME);
truncate($fh, $FILEPOS) or die $!;

# Assumes Dumper is already loaded and configured as in first code snippet
print $fh Dumper(\%HASH);

Обязательно сохраняйте резервные копии своих файлов, так как при их разработке одна ошибка может уничтожить весь ваш код!

Также обратите внимание, что то же самое будет работать с Storable; хранение будет более эффективным и быстрым. Единственное предостережение в том, что он двоичный, что означает, что файл diff, скорее всего, не будет отображаться в управлении исходным кодом, и его не так легко редактировать, как вывод Dumper.

...