perldebtut
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- use strict
- Просмотр данных и -w и v
- помощь
- Пошаговое выполнение кода
- Заполнитель для a, w, t, T
- РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
- СОВЕТЫ ПО ВЫВОДУ
- CGI
- GUIs
- РЕЗЮМЕ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- СОАВТОРЫ
ИМЯ
perldebtut - Руководство по отладке Perl
ОПИСАНИЕ
Очень краткое введение в использование отладчика Perl и указание на существующие, более глубокие источники информации по отладке программ на Perl.
Существует огромное количество людей, которые, похоже, ничего не знают об использовании отладчика Perl, хотя они используют этот язык каждый день. Это для них.
use strict
Прежде всего, есть несколько вещей, которые вы можете сделать, чтобы значительно упростить отладку программ на Perl, не используя отладчик. В качестве демонстрации, вот простой скрипт с именем "hello" с проблемой:
#!/usr/bin/perl
$var1 = 'Hello World'; # always wanted to do that :-)
$var2 = "$varl\n";
print $var2;
exit; Хотя он компилируется и выполняется без проблем, он, вероятно, не выполнит ожидаемого действия, то есть не выведет "Hello World\n"; Вместо этого он сделает именно то, что ему было сказано, компьютеры немного такие. То есть, он выведет символ новой строки, и у вас получится, как будто пустая строка. Похоже, что есть 2 переменных, когда (из-за опечатки) их на самом деле 3:
$var1 = 'Hello World';
$varl = undef;
$var2 = "\n"; Чтобы поймать этот тип проблемы, мы можем заставить каждую переменную объявляться перед использованием, включив модуль strict, поместив 'use strict;' после первой строки скрипта.
Теперь при запуске perl пожалуется на 3 необъявленные переменные, и мы получим четыре сообщения об ошибках, потому что одна переменная упоминается дважды:
Global symbol "$var1" requires explicit package name at ./t1 line 4.
Global symbol "$var2" requires explicit package name at ./t1 line 5.
Global symbol "$varl" requires explicit package name at ./t1 line 5.
Global symbol "$var2" requires explicit package name at ./t1 line 7.
Execution of ./hello aborted due to compilation errors. Превосходно! И для исправления этого мы явным образом объявляем все переменные, и теперь наш скрипт выглядит так:
#!/usr/bin/perl
use strict;
my $var1 = 'Hello World';
my $varl = undef;
my $var2 = "$varl\n";
print $var2;
exit; Затем мы делаем (всегда хорошая идея) проверку синтаксиса перед повторной попыткой запуска:
> perl -c hello
hello syntax OK И теперь, когда мы запускаем его, мы всё ещё получаем "\n", но по крайней мере, мы знаем почему. Просто компиляция скрипта выявила переменную '$varl' (с буквой 'l'), и простое изменение $varl на $var1 решит проблему.
Просмотр данных и -w и v
Хорошо, а как насчет того, когда вы хотите по-настоящему увидеть свои данные, что в динамической переменной, прямо перед её использованием?
#!/usr/bin/perl
use strict;
my $key = 'welcome';
my %data = (
'this' => qw(that),
'tom' => qw(and jerry),
'welcome' => q(Hello World),
'zip' => q(welcome),
);
my @data = keys %data;
print "$data{$key}\n";
exit; Выглядит нормально, после проверки синтаксиса (perl -c scriptname), мы запускаем его, и всё, что мы получаем, это пустая строка! Хммм.
Один из распространенных подходов к отладке здесь — щедро разбросать несколько инструкций print, чтобы добавить проверку прямо перед выводом наших данных и ещё одну после:
print "All OK\n" if grep($key, keys %data);
print "$data{$key}\n";
print "done: '$data{$key}'\n"; И попробуйте ещё раз:
> perl data
All OK
done: '' После длительного разглядывания того же куска кода и не видя леса за деревьями некоторое время, мы выпьем чашку кофе и попробуем другой подход. То есть, мы вызовем кавалерию, дав perl ключ '-d' в командной строке:
> perl -d data
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(./data:4): my $key = 'welcome'; Теперь мы запустили встроенный отладчик Perl для нашего скрипта. Он остановился на первой исполняемой строке и ждёт ввода.
Прежде чем продолжить, вы захотите узнать, как выйти из отладчика: используйте просто букву 'q', а не слова 'quit' или 'exit':
DB<1> q
> И всё, вы снова на своей территории.
помощь
Запустите отладчик снова для вашего скрипта, и мы посмотрим на меню справки. Есть несколько способов вызова справки: простая 'h' даст список справочной информации, '|h' (pipe-h) перенаправит справку на вашу программу просмотра (вероятно, 'more' или 'less'), и, наконец, 'h h' (h-space-h) покажет весь экран справки. Вот страница краткого описания:
D1h
List/search source lines: Control script execution:
l [ln|sub] List source code T Stack trace
- or . List previous/current line s [expr] Single step
[in expr]
v [line] View around line n [expr] Next, steps over
subs
f filename View source in file <CR/Enter> Repeat last n or s
/pattern/ ?patt? Search forw/backw r Return from
subroutine
M Show module versions c [ln|sub] Continue until
position
Debugger controls: L List break/watch/
actions
o [...] Set debugger options t [expr] Toggle trace
[trace expr]
<[<]|{[{]|>[>] [cmd] Do pre/post-prompt b [ln|event|sub] [cnd] Set
breakpoint
! [N|pat] Redo a previous command B ln|* Delete a/all
breakpoints
H [-num] Display last num commands a [ln] cmd Do cmd before line
= [a val] Define/list an alias A ln|* Delete a/all
actions
h [db_cmd] Get help on command w expr Add a watch
expression
h h Complete help page W expr|* Delete a/all watch
exprs
|[|]db_cmd Send output to pager ![!] syscmd Run cmd in a
subprocess
q or ^D Quit R Attempt a restart
Data Examination: expr Execute perl code, also see: s,n,t expr
x|m expr Evals expr in list context, dumps the result or lists
methods.
p expr Print expression (uses script's current package).
S [[!]pat] List subroutine names [not] matching pattern
V [Pk [Vars]] List Variables in Package. Vars can be ~pattern or
!pattern.
X [Vars] Same as "V current_package [Vars]".
y [n [Vars]] List lexicals in higher scope <n>. Vars same as V.
For more help, type h cmd_letter, or run man perldebug for all docs. Более запутанные параметры, чем можно сосчитать! Это не так плохо, как кажется, и очень полезно узнать больше о них, и даже забавно!
Есть несколько полезных, которые нужно знать сразу. Вы бы не подумали, что мы вообще используем какие-либо библиотеки, но 'M' покажет, какие модули загружены в настоящее время, и их номер версии, в то время как 'm' покажет методы, а 'S' покажет все подпрограммы (по шаблону), как показано ниже. 'V' и 'X' показывают переменные в программе по области видимости пакета и могут быть ограничены шаблоном.
DB<2>S str
dumpvar::stringify
strict::bits
strict::import
strict::unimport Использование 'X' и родственников требует от вас не использовать идентификаторы типов ($@%), а только 'название':
DM<3>X ~err
FileHandle(stderr) => fileno(2) Помните, что мы находимся в нашей крошечной программе с проблемой, мы должны посмотреть, где мы находимся, и как выглядят наши данные. Прежде всего, давайте посмотрим на код в нашем текущем положении (в данном случае первая строка кода) с помощью 'v':
DB<4> v
1 #!/usr/bin/perl
2: use strict;
3
4==> my $key = 'welcome';
5: my %data = (
6 'this' => qw(that),
7 'tom' => qw(and jerry),
8 'welcome' => q(Hello World),
9 'zip' => q(welcome),
10 ); В строке 4 есть полезная подсказка, которая говорит вам, где вы сейчас находитесь. Чтобы увидеть больше кода, снова введите 'v':
DB<4> v
8 'welcome' => q(Hello World),
9 'zip' => q(welcome),
10 );
11: my @data = keys %data;
12: print "All OK\n" if grep($key, keys %data);
13: print "$data{$key}\n";
14: print "done: '$data{$key}'\n";
15: exit; И если вам нужно снова вывести строку 5, введите 'l 5' (обратите внимание на пробел):
DB<4> l 5
5: my %data = ( В этом случае не так много, но, конечно, обычно есть страницы вещей, через которые нужно пройти, и 'l' может быть очень полезен. Чтобы переместить свой обзор на строку, которую мы собираемся выполнить, введите одиночную точку '.':
DB<5> .
main::(./data_a:4): my $key = 'welcome'; Отображаемая строка — это строка, которая будет выполнена следующей, она ещё не выполнилась. Поэтому, хотя мы можем вывести переменную с буквой 'p', в данный момент мы получим только пустое (неопределённое) значение. То, что нам нужно сделать, — это перейти к следующей исполняемой инструкции с помощью 's':
DB<6> s
main::(./data_a:5): my %data = (
main::(./data_a:6): 'this' => qw(that),
main::(./data_a:7): 'tom' => qw(and jerry),
main::(./data_a:8): 'welcome' => q(Hello World),
main::(./data_a:9): 'zip' => q(welcome),
main::(./data_a:10): ); Теперь мы можем взглянуть на первую переменную ($key):
DB<7> p $key
welcome строка 13 — это место действия, поэтому давайте перейдём туда с помощью буквы 'c', которая, кстати, вставляет одноразовый контрольный пункт в данной строке или подпрограмме:
DB<8> c 13
All OK
main::(./data_a:13): print "$data{$key}\n"; Мы прошли проверку (где выводился 'All OK') и остановились прямо перед основной задачей. Мы могли бы попробовать вывести несколько переменных, чтобы посмотреть, что происходит:
DB<9> p $data{$key} Ничего особенного, давайте посмотрим на наш хэш:
DB<10> p %data
Hello Worldziptomandwelcomejerrywelcomethisthat
DB<11> p keys %data
Hello Worldtomwelcomejerrythis Ну, это не очень легко читается, и с помощью полезного руководства (h h), команда 'x' выглядит многообещающе:
DB<12> x %data
0 'Hello World'
1 'zip'
2 'tom'
3 'and'
4 'welcome'
5 undef
6 'jerry'
7 'welcome'
8 'this'
9 'that' Это не очень помогает, несколько приветствий там, но нет никаких указаний, какие ключи, а какие значения, это просто выведенный массив, и в этом случае не особенно полезно. Секрет здесь заключается в использовании ссылки на структуру данных:
DB<13> x \%data
0 HASH(0x8194bc4)
'Hello World' => 'zip'
'jerry' => 'welcome'
'this' => 'that'
'tom' => 'and'
'welcome' => undef Ссылка действительно выводится, и мы наконец можем увидеть, с чем имеем дело. Наше цитирование было совершенно допустимым, но неправильным для наших целей, при этом 'and jerry' рассматривалось как 2 отдельных слова, а не фраза, что сбивает с толку правильно спаренную структуру хэша.
Переключатель '-w' сказал бы нам об этом, если бы мы использовали его в начале, и сэкономили бы нам много проблем:
> perl -w data
Odd number of elements in hash assignment at ./data line 5. Мы исправляем цитирование: 'tom' => q(and jerry) и запускаем его снова, на этот раз мы получаем ожидаемый вывод:
> perl -w data
Hello World Пока мы здесь, давайте внимательнее посмотрим на команду 'x', она действительно полезна и будет с удовольствием выводить вложенные ссылки, полные объекты, частичные объекты — почти всё, что вы бросите в неё:
Давайте быстро создадим объект и разобьём его, сначала запустим отладчик: он хочет некоторый вид ввода из STDIN, поэтому мы даём ему что-то не обязывающее, ноль:
> perl -de 0
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(-e:1): 0 Теперь создайте на лету объект в течение нескольких строк (обратите внимание на обратный слэш):
DB<1> $obj = bless({'unique_id'=>'123', 'attr'=> \
cont: {'col' => 'black', 'things' => [qw(this that etc)]}}, 'MY_class') И давайте посмотрим на него:
DB<2> x $obj
0 MY_class=HASH(0x828ad98)
'attr' => HASH(0x828ad68)
'col' => 'black'
'things' => ARRAY(0x828abb8)
0 'this'
1 'that'
2 'etc'
'unique_id' => 123
DB<3> Полезно, да? Вы можете оценить почти всё в нём и экспериментировать с фрагментами кода или регулярными выражениями до бесконечности:
DB<3> @data = qw(this that the other atheism leather theory scythe)
DB<4> p 'saw -> '.($cnt += map { print "\t:\t$_\n" } grep(/the/, sort @data))
atheism
leather
other
scythe
the
theory
saw -> 6 Если вы хотите увидеть историю команд, введите 'H':
DB<5> H
4: p 'saw -> '.($cnt += map { print "\t:\t$_\n" } grep(/the/, sort @data))
3: @data = qw(this that the other atheism leather theory scythe)
2: x $obj
1: $obj = bless({'unique_id'=>'123', 'attr'=>
{'col' => 'black', 'things' => [qw(this that etc)]}}, 'MY_class')
DB<5> И если вы хотите повторить любую предыдущую команду, используйте восклицательный знак: '!':
DB<5> !4
p 'saw -> '.($cnt += map { print "$_\n" } grep(/the/, sort @data))
atheism
leather
other
scythe
the
theory
saw -> 12 Для получения дополнительной информации о ссылках см. perlref и perlreftut
Пошаговое выполнение кода
Вот простая программа, которая преобразует Цельсия в Фаренгейт, у неё тоже есть проблема:
#!/usr/bin/perl -w
use strict;
my $arg = $ARGV[0] || '-c20';
if ($arg =~ /^\-(c|f)((\-|\+)*\d+(\.\d+)*)$/) {
my ($deg, $num) = ($1, $2);
my ($in, $out) = ($num, $num);
if ($deg eq 'c') {
$deg = 'f';
$out = &c2f($num);
} else {
$deg = 'c';
$out = &f2c($num);
}
$out = sprintf('%0.2f', $out);
$out =~ s/^((\-|\+)*\d+)\.0+$/$1/;
print "$out $deg\n";
} else {
print "Usage: $0 -[c|f] num\n";
}
exit;
sub f2c {
my $f = shift;
my $c = 5 * $f - 32 / 9;
return $c;
}
sub c2f {
my $c = shift;
my $f = 9 * $c / 5 + 32;
return $f;
} По какой-то причине преобразование Фаренгейта в Цельсия не возвращает ожидаемый результат. Вот что он делает:
> temp -c0.72
33.30 f
> temp -f33.3
162.94 c Не очень последовательно! Мы установим контрольный пункт в коде вручную и запустим его в отладчике, чтобы посмотреть, что происходит. Контрольный пункт — это метка, до которой отладчик выполнится без прерывания, когда он достигнет контрольного пункта, он остановит выполнение и предложит подсказку для дальнейшего взаимодействия. В обычном использовании эти команды отладчика полностью игнорируются, и они безопасны — хоть и немного беспорядочны — для использования в рабочем коде.
my ($in, $out) = ($num, $num);
$DB::single=2; # insert at line 9!
if ($deg eq 'c')
...
> perl -d temp -f33.3
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or `h h' for help, or `man perldebug' for more help.
main::(temp:4): my $arg = $ARGV[0] || '-c100'; Мы просто продолжим до нашего установленного контрольного пункта с помощью 'c':
DB<1> c
main::(temp:10): if ($deg eq 'c') { За этим следует команда просмотра, чтобы увидеть, где мы находимся:
DB<1> v
7: my ($deg, $num) = ($1, $2);
8: my ($in, $out) = ($num, $num);
9: $DB::single=2;
10==> if ($deg eq 'c') {
11: $deg = 'f';
12: $out = &c2f($num);
13 } else {
14: $deg = 'c';
15: $out = &f2c($num);
16 } И вывод, чтобы показать, какие значения мы в настоящее время используем:
DB<1> p $deg, $num
f33.3 Мы можем установить ещё один контрольный пункт в любой строке, начинающейся с двоеточия, мы будем использовать строку 17, так как это как раз при выходе из подпрограммы, и мы хотели бы там позже приостановиться:
DB<2> b 17 Ответа нет, но вы можете увидеть установленные контрольные пункты с помощью команды 'L':
DB<3> L
temp:
17: print "$out $deg\n";
break if (1) Обратите внимание, что для удаления контрольного пункта используется 'B'.
Теперь мы продолжим в нашу подпрограмму, но на этот раз, вместо номера строки, мы будем использовать имя подпрограммы, за которым следует уже знакомая нам команда 'v':
DB<3> c f2c
main::f2c(temp:30): my $f = shift;
DB<4> v
24: exit;
25
26 sub f2c {
27==> my $f = shift;
28: my $c = 5 * $f - 32 / 9;
29: return $c;
30 }
31
32 sub c2f {
33: my $c = shift; Обратите внимание, что если между нами и строкой 29 была вызов подпрограммы, и мы хотели бы пошагово пройти её, мы могли бы использовать команду 's', а чтобы перейти через неё, мы бы использовали 'n', которая выполнила бы подпрограмму, но не спустилась в неё для проверки. Однако в данном случае мы просто перейдём до строки 29:
DB<4> c 29
main::f2c(temp:29): return $c; И посмотрим на возвращаемое значение:
DB<5> p $c
162.944444444444 Это совершенно неверный ответ, но сумма выглядит правильно. Интересно, связано ли это с приоритетом операторов? Мы попробуем несколько других возможностей с нашей суммой:
DB<6> p (5 * $f - 32 / 9)
162.944444444444
DB<7> p 5 * $f - (32 / 9)
162.944444444444
DB<8> p (5 * $f) - 32 / 9
162.944444444444
DB<9> p 5 * ($f - 32) / 9
0.722222222222221 :-) это больше похоже на правду! Хорошо, теперь мы можем установить нашу переменную возврата, и мы вернёмся из подпрограммы с помощью 'r':
DB<10> $c = 5 * ($f - 32) / 9
DB<11> r
scalar context return from main::f2c: 0.722222222222221 Выглядит хорошо, давайте просто продолжим до конца скрипта:
DB<12> c
0.72 c
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info. Быстрое исправление ошибочной строки (вставьте недостающие скобки) в реальной программе, и всё готово.
Placeholder for a, w, t, T
Действия, наблюдение за переменными, трассировки стека и т. д.: в списке дел.
a
w
t
T РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
Хотели узнать, как выглядит регулярное выражение? Для этого вам понадобится Perl, скомпилированный со флагом DEBUGGING:
> perl -Dr -e '/^pe(a)*rl$/i'
Compiling REx `^pe(a)*rl$'
size 17 first at 2
rarest char
at 0
1: BOL(2)
2: EXACTF <pe>(4)
4: CURLYN[1] {0,32767}(14)
6: NOTHING(8)
8: EXACTF <a>(0)
12: WHILEM(0)
13: NOTHING(14)
14: EXACTF <rl>(16)
16: EOL(17)
17: END(0)
floating `'$ at 4..2147483647 (checking floating) stclass
`EXACTF <pe>' anchored(BOL) minlen 4
Omitting $` $& $' support.
EXECUTING...
Freeing REx: `^pe(a)*rl$' Вы действительно хотели это узнать? :-) Для получения более подробных сведений о работе с регулярными выражениями, обратитесь к perlre, perlretut, а для расшифровки загадочных меток (BOL и CURLYN и т. д. выше), см. perldebguts.
СОВЕТЫ ПО ВЫВОДУ
Чтобы получить весь вывод из файла журнала ошибок и не пропустить никаких сообщений из-за буферизации операционной системы, вставьте в начало вашего скрипта строку:
$|=1; Для просмотра хвоста динамически растущего файла журнала (из командной строки):
tail -f $error_log Оборачивание всех вызовов die в обработчике может быть полезно, чтобы увидеть, как и откуда они вызываются. perlvar содержит дополнительную информацию:
BEGIN { $SIG{__DIE__} = sub { require Carp; Carp::confess(@_) } } Различные полезные приёмы перенаправления файловых дескрипторов STDOUT и STDERR описаны в perlopentut и perlfaq8.
CGI
Вот быстрый совет для всех программистов CGI, которые не могут понять, как обойти приглашение «ожидание ввода» при запуске скрипта CGI из командной строки. Попробуйте что-нибудь вроде этого:
> perl -d my_cgi.pl -nodebug Конечно, CGI и perlfaq9 расскажут вам больше.
Графические интерфейсы
Интерфейс командной строки тесно интегрирован с расширением emacs, а также имеется интерфейс vi.
Однако вам не обязательно всё делать в командной строке. Существуют некоторые варианты графического интерфейса. Преимущество заключается в том, что вы можете навести курсор мыши на переменную, и в соответствующем окне или всплывающем сообщении появится её данные. Больше нет необходимости в утомительном наборе «x $varname» :-)
В частности, обратите внимание на следующее:
ptkdb обертка perlTK для встроенного отладчика
ddd отладчик отображения данных
PerlDevKit и PerlBuilder специфичны для NT
Примечание. (Были бы полезны дополнительные сведения об этих и других).
РЕЗЮМЕ
Мы увидели, как использовать хорошие практические приёмы кодирования с use strict и -w. Мы можем запустить отладчик Perl perl -d scriptname, чтобы просмотреть ваши данные изнутри отладчика Perl с помощью команд p и x. Вы можете пройтись по вашему коду, установить точки останова с помощью b и перейти по коду с помощью s или n, продолжить с c и вернуться из подпрограммы с r. Достаточно интуитивно, когда вы дойдёте до этого.
Конечно, есть ещё много чего узнать, это только поверхностный взгляд. Лучший способ узнать больше — использовать perldoc для получения дополнительной информации о языке, прочитать онлайн-справочную информацию (perldebug — вероятно, следующее место, куда следует отправиться), и, конечно, экспериментировать.
См. также
perldebug, perldebguts, perl5db.pl, perldiag, perlrun
АВТОР
Ричард Фоли <richard.foley@rfi.net> Авторское право (c) 2000
СОАВТОРЫ
Различные люди внесли полезные предложения и дополнения, в частности:
Рональд Дж. Кимбалл <rjk@linguist.dartmouth.edu>
Хьюго ван дер Санден <hv@crypt0.demon.co.uk>
Питер Скотт <Peter@PSDT.com>
© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.34.0/perldebtut