2013-04-16 5 views
4

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

я написал следующее:

use 5.16.3; 
use strict; 
use Getopt::Long; 

Getopt::Long::Configure(qw(bundling no_getopt_compat)); 
&ArgParser; 
our ($sqluser,$sqlpass); 

$sqluser="root"; 
$sqlpass="mypassword"; 

sub ArgParser { 
    print "Username is ".$sqluser." Password is ".$sqlpass."\n"; 
    my $crt=''; 
    my $delete=''; 
    GetOptions ('create|c=s' => \$crt, 
     'delete|d=s' => \$delete 
    ); 
    if ($crt) { 
     &DatabaseExec("create",$crt); 
    } elsif ($delete) { 
     &DatabaseExec("delete",$delete);  
    } else { 
    print "No options chosen\n"; 
    } 
} 

sub DatabaseExec { 
    use DBI; 
    my $dbname=$_[1]; 
    print "Username is ".$sqluser." Password is ".$sqlpass."\n"; 
    my $dbh = DBI->connect("dbi:mysql:", $sqluser,$sqlpass); 
    my $comand=$_[0]; 
    if ($_[0] eq "create") { 
     my $db_com="create database ".$dbname; 
     print 1 == $dbh->do($db_com) ? "Database created\n":"An error occured while creating database. Maybe it exists?\n"; 
     #print "Executing: ".$db_com."\n"; 
    } elsif ($_[0] eq "delete") { 
     my $db_com="DROP DATABASE ".$dbname; 
     #print "Executing: ".$db_com."\n"; 
     print 1 == $dbh->do($db_com) ? "Database deleted\n":"An error occured while creating database. Maybe it exists?\n"; 
    } 
} 

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

#~/perlscripts/dbtest.pl -c hellos 
Use of uninitialized value $sqluser in concatenation (.) or string at /root/perlscripts/dbtest.pl line 20. 
Use of uninitialized value $sqlpass in concatenation (.) or string at /root/perlscripts/dbtest.pl line 20. 
Username is Password is 
Use of uninitialized value $sqluser in concatenation (.) or string at /root/perlscripts/dbtest.pl line 44. 
Use of uninitialized value $sqlpass in concatenation (.) or string at /root/perlscripts/dbtest.pl line 44. 
Username is Password is 
DBI connect('','',...) failed: Access denied for user 'root'@'localhost' (using password: NO) at /root/perlscripts/dbtest.pl line 45. 
Can't call method "do" on an undefined value at /root/perlscripts/dbtest.pl line 50. 

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

+2

Вы используете скрипты perl как root, не зная точно, что вы делаете? :) – TLP

+0

Ну, да, но это VPS, на котором я делаю ничего, кроме теста perl, и скачиваю торренты. :) – Droidzone

+1

Тем не менее, это небольшая мера предосторожности для создания нового пользователя с ограниченными привилегиями. – TLP

ответ

11

Ваши переменные не объявляются, когда ваша подпрограмма вызывается:

&ArgParser;     # subroutine call 
our ($sqluser,$sqlpass); # declaration 

$sqluser="root";   # assignment 
$sqlpass="mypassword"; 

Для того, чтобы использовать эти глобальные переменные внутри подпрограммы, поместите подпрограмму после объявления переменной.

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

my $sqluser = "root"; 
my $sqlpass = "mypass"; 

ArgParser($sqluser, $sqlpass); # you should not use & in subroutine calls 

, а затем внутри подпрограммы:

sub ArgParser { 
    my ($sqluser, $sqlpass) = @_; 
    ... 

Таким образом, ваши переменные красиво инкапсулируются и защищены от манипуляций случайно.

Что касается амперсанд & в вашем вызове подпрограммы, это описано в perldoc perlsub:

To call subroutines: 

NAME(LIST); # & is optional with parentheses. 
NAME LIST; # Parentheses optional if predeclared/imported. 
&NAME(LIST); # Circumvent prototypes. 
&NAME;  # Makes current @_ visible to called subroutine. 
+0

Если я не использую амперсанд, я получаю следующее: «Запрещено« АргарПарсер »Bareword« не разрешено, пока «строгие субтитры» используются в строке ./dbtest.pl 13. ». – Droidzone

+1

@Droidzone Это потому, что вы не читали документацию, которую я так задумчиво вложил в свой ответ за вас. 'ArgParser;' не входит в список допустимых способов вызова подпрограммы, если вы не запустили подпрограмму. Используйте «ArgParser()» с круглыми скобками. – TLP

+0

Извините, теперь я понимаю. Я использую субмарины, не объявляя их некоторое время. – Droidzone

0

Классическим ресурс по видимости переменной, которую я рекомендую прочитать, это Coping with scoping Марк-Jason Dominus: он описывает фундаментальный раздел в семействах переменных Perl (пакетные и лексические переменные) и предупреждает о некоторых плохих практиках, которые может возникнуть у новичка.

+1

Спасибо, но разве это немного устарело? Он не ссылается на более новый «наш». – Droidzone

+0

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

+0

['our'] (http://perldoc.perl.org/functions/our.html« perldoc -f our ») на самом деле не такой новый, он был в Perl [начиная с 5.6.0 еще в 2000 году] (http://perldoc.perl.org/perlhist.html "perldoc perlhist"). –

5

У Perl нет глобальных переменных. Что такое Perl:

  • Переменные упаковки.
  • Пересекающиеся по левому краю переменные.

A пакет пространство имен. В Perl пространство имен иногда называют пакетом . Ваше имя по умолчанию - main. Например. Это совершенно законно:

use strict; 
use warnings; 

$main::variable = "What? Where's my 'our' or 'my' declaration?"; 

print "Look, I can print $main::variable without using 'my' or 'our'!"; 

Я просто префикс моих имен переменных пакета с пакетом, и Wham! Они существуют!

Это вызывает у меня испуг:

use strict; 
use warnings; 

$variable = "What? Where's my 'our' or 'my' declaration?"; 

print "I'm not going to print 'cause you're going to get a compilation error"; 

С use strict;, вы должны либо объявить переменную как our или my, или префикс с именем пакета он находится в

. Переменные пакета легче всего понять. переменные пакета на самом деле хранятся в переменной структуры Perl, поэтому они всегда доступны однажды заявил:

use strict; 
use warnings; 

if (1 == 1) { #Yes, I know this is always true 
    our $foo = "I have a value!"; 
} 

say "Looks like $foo has a value"; 

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

use strict; 
use warnings; 

my $foo = "Foo has a value"; 

if ($foo) { #Always true 
    my $bar = "bar has a value"; 
    print "$foo\n"; # $foo has a value. This is a sub-block 
    print "$bar\n"; # $bar has a value. It was defined in this block 
} 

print "$foo\n"; # $foo still has a value. 
print "$bar\n"; # You'll get en error here. $bar out of scope here 

Вот несколько предложений:

  • Вам не нужно predeclare подпрограмм. Это просто просит неприятностей.
  • Если вы определяете свои переменные в начале вашей программы, вы можете использовать переменные my, и они будут доступны в ваших подпрограммах, так как они все еще будут в области видимости.
  • Оставьте & сбой вызовов подпрограммы. Они вызывают незначительные изменения в способе работы подпрограммы, и эти тонкие изменения, вероятно, не то, что вы хотите. Стандарт - это просто вызов подпрограммы.
    • Избегайте ? ... : ..., особенно если вы не используете пробелы. Это затрудняет чтение вашей программы и не сохраняет время выполнения.
    • Вывести параметр подпрограммы в переменные, как только вы вызовете свою подпрограмму.
  • Perl интерполирует переменные для вас. С Perl много проблем. Он не имеет реальной объектной ориентации. Это не язык, ориентированный на исключение. У него много крутизны. Одним из больших преимуществ является то, что вам не нужно проходить всевозможные махинации для печати значения переменной. Используйте его и держите голову в гордости, когда вы вешаете фанатов Python.
  • Используйте пробелы, чтобы сделать код более удобным для чтения.
  • Возможно, что вы действительно хотите: константы. my будут работать, но константы гарантируют, что эти значения не будут случайно изменены в вашей программе.

Вот ваш код переписан. Обратите внимание, что константы не имеют сигилы спереди. Они обычно не могут быть интерполированы в строках. Однако, если вы окружите их @{[...]}, вы также можете их интерполировать. Я сделал это в обоих направлениях:

use 5.16.3; 
use strict; 
use Getopt::Long; 

use constant { 
    SQL_USER => "root", 
    SQL_PASS => "mypassword", 
}; 

Getopt::Long::Configure qw(bundling no_getopt_compat); 

sub ArgParser { 
    print "Username is " SQL_USER . " Password is " . SQL_PASS . "\n"; 
    my $crt; 
    my $delete; 
    GetOptions (
     'create|c=s' => \$crt, 
     'delete|d=s' => \$delete, 
    ); 
    if ($crt) { 
     DatabaseExec("create", $crt); 
    } 
    elsif ($delete) { 
     DatabaseExec("delete", $delete);  
    } 
    else { 
     print "No options chosen\n"; 
    } 
} 

sub DatabaseExec { 
    use DBI; 

    my $comand = shift; 
    my $dbname = shift; 

    print "Username is @{[SQL_USER]} Password is @{[SQL_PASS]}\n"; 

    my $dbh = DBI->connect(
     "dbi:mysql:", 
     SQL_USER, 
     SQL_PASS 
    ); 

    if ($command eq "create") { 
     my $db_com = "create database $dbname"; 
     if ($dbh->do($db_com)) { 
      print "Database created\n" 
     } 
     else { 
      print "An error occured while creating database. Maybe it exists?\n"; 
     } 
    } elsif ($command eq "delete") { 
     my $db_com = "DROP DATABASE $dbname"; 
     #print "Executing: ".$db_com."\n"; 
     if ($dbh->do($db_com)) { 
      print "Database deleted\n"; 
     } 
     else { 
      print "An error occured while creating database. Maybe it exists?\n"; 
     } 
    } 
} 
+0

Удивительное объяснение. Большое спасибо за правильные рекомендации по программированию! – Droidzone

Смежные вопросы