Как сохранить скалярное значение при выполнении программы? - PullRequest
2 голосов
/ 27 июля 2011

Пример кода var_inc1.pl:

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

Вывод:

  • впервые: perl var_inc1.pl

    value : 1
    
  • во второй раз: perl var_inc1.pl

    value : 1
    

Но я хочу, чтобы вывод был

  • Первое выполнение: perl var_inc1.pl

    value : 1
    
  • во второй раз: perl var_inc1.pl

    value : 2
    
  • в третий раз: perl var_inc1.pl

    value : 3
    

… и т. Д., Где скалярное значение увеличивается при каждом выполнении программы.

Ответы [ 3 ]

8 голосов
/ 27 июля 2011

Как только Perl-программа заканчивается, все в оперативной памяти исчезает. Если вы хотите, чтобы значение сохранялось между запусками программы, вам нужна некоторая форма постоянного хранения. Какой тип хранилища вам нужен, зависит от того, какие ограничения вашей среды вам нужны и какая форма сохранения вам нужна (если разные пользователи увидят одно и то же значение и смогут его изменить, как долго он должен жить и т. Д.).

Самый простой (но не самый лучший) способ получить постоянство в Perl 5 - использовать функцию dbmopen для создания / открытия файла DBM, связанного с хешем:

#!/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\n";

В общем случае dbmopen было заменено на tie, что является более общим способом связывания кода с переменными. Более современный подход к приведенному выше коду:

#!/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\n";

Иногда вы не хотите зависеть от внешнего ресурса, в таких случаях вы можете просто написать самоизменяющийся скрипт:

#!/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\n"; #save the current value

print "$x\n";

__DATA__
1

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

Конечно, вы также можете использовать реляционную базу данных:

#!/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\n";

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

$dbh->disconnect;
5 голосов
/ 27 июля 2011

Проблема заключается в запоминании значения между вызовами программы.

На ум приходят две техники:

  1. Вы можете заставить вызывающего запомнить значение и передать его скрипту Perl в качестве аргумента. Так поддерживается состояние в веб-приложениях. Если у вас разные абоненты, вызывающие одну и ту же программу, у каждого будет свой счетчик.

  2. Вы можете записать и прочитать значение в / из постоянного хранилища, например, в файл. Если у вас есть несколько разных вызывающих абонентов, вызывающих программу Perl, это сработает для вызова общего количества вызовов, но если можно одновременно выполнить несколько вызовов, вы должны позаботиться об открытии хранилища для записи таким образом, чтобы только один вызов мог иметь он открывается одновременно; см. например perlopentut , если вы хотите использовать файл.

0 голосов
/ 27 июля 2011

Вы можете посмотреть на DBM::Deep, который может связать базу данных с переменной. Вы используете его как обычный Perl scalar хеш или массив, но он автоматически сохраняет и извлекает значения, используя базу данных с плоскими файлами. Изменить: Скаляры, по-видимому, не включены.

Вот пример использования хэша для эмуляции вашего примера.

#!/usr/bin/env perl

use strict;
use warnings;

use DBM::Deep;

my %x;
tie %x, 'DBM::Deep', 'test.db';

$x{'x'}++;
print "value : ". $x{'x'} ."\n";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...