I was curious if using Storable's store_fd and fd_retrieve would allow me to store a data structure into a program's own DATA filehandle. I realize this isn't Best Practice, I'm just curious if it'd work, my quick attempts to try it don't seem to work.
-
I would expect to fd_retreive to work, and store_fd to fail. – Evan Carroll Feb 05 '10 at 20:24
-
What purpose do you have in mind? – Greg Bacon Feb 05 '10 at 22:09
-
1DATA is usually only open for input; try Inline::Files? – ysth Feb 06 '10 at 00:35
-
The purpose is simply to see If Perl Can. It simply for amusements sake. From the comments I understand that `DATA` cannot be written to? – Danny Feb 06 '10 at 00:57
-
* That would explain the hoops Inline::File jumps through to write. – Danny Feb 06 '10 at 01:09
4 Answers
I'm not sure why you'd want to do that, but you can fake it. You should try to avoid that though.
Just for giggles, you could open a filehandle, read lines from $0
and print them until you get to __DATA__
, then add your new __DATA__
section. The trick is then to rename your new file to become $0
, perhaps by an exec
if your system locks the file while the program is running:
#!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

- 129,424
- 31
- 207
- 592
DATA
is a handle to read data stored with the script. Conway's Inline::Files is the only module I know offhand that talks of writable virtual files. And since script files are usually ASCII I don't know what would happen if you got a binary 26 byte on MSDOS or a binary 4 on UNIX in the output of Storable.
However, if you are talking about you storing data by typing it in there, only to read it from a script, then the binary problem still confronts you.
So, it's better to go with YAML or JSON for persistence. I know that YAML will handle the blessing when retrieving the data from DATA.

- 29,660
- 2
- 47
- 102
It's not possible in ordinary conditions:
$ cat write-data
#! /usr/bin/perl
use warnings;
print DATA "bar!\n";
$ ./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.
But you can create extraordinary conditions:
#! /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;
On Windows, you'll get a warning on the unlink
because of its default file-sharing semantics. If that's your platform, you can clean up at END
time or use Win32::SharedFileOpen.

- 134,834
- 32
- 188
- 245
I came up with my own solution... not that I specifically recommend it or any others; in my case it's a unit test script with reference values in a multi-dimensional hash structure. I won't go into the technical details of what this things does and why, but the end result is that a small fix or change in the code can result in many values requiring an update (after validating the change is valid).
Therefore I moved the hash into the __DATA__
section using Data::Dumper
. The code to write that to a file handle looks like:
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);
At the beginning of the script I load the hash from DATA
after storing references to the initial handle's position and mtime (mtime is used to ensure the file hasn't been modified during the script's execution).
use vars qw(%HASH $FILEPOS $MTIME);
{
$FILEPOS = tell(DATA);
$MTIME = (stat(DATA))[9];
local $/;
my $data = <DATA>;
%HASH = %{eval $data};
}
Finally, to update the __DATA__
section I open __FILE__
at $FILEPOS
, truncate it and write to it. I simplified error handling for this example.
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);
Be sure to keep backups of your files as you develop as a single mistake could destroy all your code!
Also note that the same would work with Storable
; storage would be more efficient and faster. The only caveat is that it's binary, which means file diff will likely not show up in source control, and it's not as easy to edit as Dumper's output.

- 2,251
- 22
- 17