Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
614 views
in Technique[技术] by (71.8m points)

perl - How Do I Persist A Scalar Value Across Program Executions?

Code sample var_inc1.pl:

#!/usr/bin/perl -w
my $x = 0;
$x++;
print "value : ".$x."
";

Output:

  • first time: perl var_inc1.pl

    value : 1
    
  • second time: perl var_inc1.pl

    value : 1
    

But I want the output to be

  • first time execution: perl var_inc1.pl

    value : 1
    
  • second time execution: perl var_inc1.pl

    value : 2
    
  • third time execution: perl var_inc1.pl

    value : 3
    

… and so on, where the scalar value increments at each program execution.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Once a Perl program ends, everything in RAM goes away. If you want a value to be retained between runs of a program you need some form of persistant storage. What sort of storage you need depends on what you the constraints of you environment and what form of persistance you need (should different users see the same value and be able to change it, how long should it live, etc.).

The simplest (but not the best) way to get persistence in Perl 5 is to use the dbmopen function to create/open a DBM file associated with a hash:

#!/usr/bin/perl

use strict;
use warnings;

dbmopen my %storage, "/tmp/foo.db", 0666 #anyone can write to it
    or die "could not open /tmp/foo.db: $!";

my $x = ++$storage{x};

print "$x
";

In general, dbmopen has been replaced by tie, which is a more general way of associating code with variables. A more modern approach to the code above would be:

#!/usr/bin/perl

use strict;
use warnings;

use DB_File;

tie my %storage, "DB_File", "/tmp/bar.db"
    or die "could not open /tmp/bar.db: $!";

my $x = ++$storage{x};

print "$x
";

Sometimes you don't want to be dependent on an external resource, in those cases you can just write a self modifying script:

#!/usr/bin/perl

use strict;
use warnings;

my $pos = tell DATA;

my $x = <DATA>;

$x++;

open DATA, "+<", $0
    or die "could not open $0 in read/write mode: $!";

seek DATA, $pos, 0
    or die "could not seek to $pos in $0";

print DATA "$x
"; #save the current value

print "$x
";

__DATA__
1

Note, this only works if all the users who are going to run this script have write permission to the script. This is a security issue if more than one user is allowed to run the script (because one user could modify the script to include malicious code which would be run by the other users).

Of course, you could also use a relational database:

#!/usr/bin/perl

use strict;
use warnings;

use DBI;

my $db = "/tmp/baz.db";

my $dbh = DBI->connect(
    "dbi:SQLite:dbname=$db",
    "", # SQLite doesn't do auth, so make sure the file
    "", # permissions are what you need them to be
    {
        AutoCommit       => 1,
        PrintError       => 0,
        RaiseError       => 1,
        ChopBlanks       => 1,
        FetchHashKeyName => "NAME_lc",
    }
) or die "could not connect to $db: ", DBI->errstr;

my $count = $dbh->selectcol_arrayref("
    SELECT count(*)
    FROM sqlite_master
    WHERE type='table'
    AND name='counter';
")->[0];

unless ($count) {
    $dbh->do("
        CREATE TABLE counter (
        name  char(50),
        value int
        );
    ");
    $dbh->do("INSERT INTO counter (name, value) VALUES ('x', 0)");
}

my $x = $dbh->selectcol_arrayref("
    SELECT value
    FROM counter
    WHERE name = 'x'
")->[0];

$x++;

print "$x
";

$dbh->do("UPDATE counter SET value = ? WHERE name = ?", {}, $x, "x");

$dbh->disconnect;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.8k users

...