perldebtut
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- use strict
- Просмотр данных и -w и v
- help
- Пошаговый просмотр кода
- Заполнитель для a, w, t, T
- РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
- СОВЕТЫ ПО ВЫВОДУ
- CGI
- Графические оболочки (GUI)
- РЕЗЮМЕ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- СОАВТОРЫ
ИМЯ
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
> Вот и всё, вы снова на родной земле.
help
Запустите отладчик снова на вашем скрипте, и мы рассмотрим меню справки. Есть несколько способов вызвать справку: простая команда 'h' даст сводку по справке, '|h' (pipe-h) направит справку на ваш диспетчер страниц (вероятно, 'more' или 'less'), а 'h h' (h-пробел-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"; Мы прошли нашу проверку (где выводилась надпись "Всё ОК") и остановились непосредственно перед основным заданием. Мы могли бы попробовать вывести несколько переменных, чтобы увидеть, что происходит:
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 Ссылка действительно выведена, и мы, наконец, можем увидеть, с чем мы имеем дело. Наша цитирование была совершенно допустимой, но неверной для наших целей, так как "и джерри" обрабатывались как 2 отдельных слова, а не как фраза, что выбило равномерно спаренную структуру массива из строя.
Переключатель '-w' показал бы нам об этом, если бы мы использовали его в начале, и сэкономил бы нам много проблем:
> perl -w data
Odd number of elements in hash assignment at ./data line 5. Мы исправляем наше цитирование: 'том' => q(и джерри) и запускаем его снова, на этот раз мы получаем ожидаемый вывод:
> 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
use v5.36;
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. Небольшое исправление в проблемной строке (добавление недостающих скобок) в исходной программе, и мы закончили.
Заполнитель для 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 расскажут вам больше.
GUI
Интерфейс командной строки тесно интегрирован с расширением emacs, и также есть интерфейс vi.
Однако вам необязательно делать все это в командной строке, существуют несколько вариантов GUI. Преимущество этих вариантов в том, что вы можете навести курсором мыши на переменную, и данные этой переменной будут отображены в соответствующем окне или всплывающем сообщении, больше не нужно утомлять себя набором «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 <richard.foley@rfi.net> Авторское право (c) 2000
СОАВТОРЫ
Различные люди внесли полезные предложения и contributions, в частности:
Ronald J Kimball <rjk@linguist.dartmouth.edu>
Hugo van der Sanden <hv@crypt0.demon.co.uk>
Peter Scott <Peter@PSDT.com>
© 1993–2023 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.38.0/perldebtut