NAME/НАИМЕНОВАНИЕ
perldebtut - Учебник по отладке Perl кода
ОПИСАНИЕ
(Очень) упрощенное введение в использование perl отладчика (perl debugger) и указатель на существующие, более глубокие источники информации по вопросу отладки Perl программ.
Существует невероятное множество людей, которые даже не слышали ничего об отладчике perl, хотя они используют этот язык каждый день. Это для них.
use strict
Во-первых есть несколько вещей, которые вы можете сделать, чтобы сделать вашу жизнь намного проще , когда речь заходит об отладке perl программ, без использования отладчика вовсе. Чтобы это показать, вот простой скрипт, с именем "hello" с проблемой:
#!/usr/bin/perl
$var1 = 'Hello World'; # всегда хотел сделать это :-)
$var2 = "$varl\n";
print $var2;
exit;
Хотя он компилируется и выполняется успешно, он, вероятно не будет делать то, что мы ожидаем, а именно, он не напечатает "Hello World\n" вовсе; с другой стороны он сделает именно то, что ему было приказано, компьютеры склонны делать то, что им говорят. Он распечатает символ новой строки, и вы получите то, что выглядит, как пустая строка. Все выглядит так, как будто у вас есть 2 переменные, тогда как (из-за опечатки) их на самом деле 3:
$var1 = 'Hello World';
$varl = undef;
$var2 = "\n";
Чтобы поймать такого рода проблемы, мы может заставить, чтобы каждая переменная быть объявлена перед использованием, включив модуль, следящий за строгим стилем программирования, поставив '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: ''
После долгого времени вглядывания в один и тот и тот же кусок кода и не видя леса за деревьями, мы выпили чашку кофе и попробовали другой подход. Что ж, придется применить кавалерию, перл нам дает ключ для командной строки '-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' (пайп-h) запустит просмотр помощи через программу просмотра (вероятно 'more' или 'less') (не работает под Windows, как среду без поддержки пайпов) и, наконец, 'h h' (h-пробел-h) выведет вам все команды с описанием. Вот суммарный список команд:
DB<1>h
Показ/поис строка исходного кода: Команды управления выполнением:
l [ln|sub] Показать исходный код T Трассировка стека
- или . Предыдущая/текущая строка s [expr] Один шаг [in expr] (single)
v [line] Строки вокруг n [expr] Следующий, не заходя в процедуры (next)
f filename Исходный текст файла <CR/Enter> Повторить последние n или s
/pattern/ ?patt? Поиск вперед/назад r Вернуться из процедуры (return)
M Показать версии модулей c [ln|sub] Продолжить до позиции (continue)
Управление отладчиком: L Посмотреть на точки останова(break)/слежка (watch)/действия(actions) (list)
o [...] Установка опций отладчика t [expr] Переключатель трассировки [trace expr]
<[<]|{[{]|>[>] [cmd] Пре/после промпт b [ln|event|sub] [cnd] Установить точку останова (breakpoint)
! [N|pat] Повторить предыдущую команд B ln|* Удалить одну/все точки останова
H [-num] Показ последних num комманд a [ln] cmd Выполнить cmd (команду ОС) после строки
= [a val] Определить псевдонимы A ln|* Удалить одно/все действия
h [db_cmd] Получить справку по команде w expr Добавить наблюдение за выражением
h h Полная страница справки W expr|* Удалить одно/все наблюдаемые выражения
|[|]db_cmd Вывести output на листер ![!] syscmd Выполнить cmd в подпроцессе
q или ^D Выход R Попытка перезагрузки
Изучение данных: expr Выполнение кода perl, также см.: s,n,t expr
x|m expr Выполняет выражение в списочном контексте, выводит результат или список методов.
p expr Печатает выражение (сценарий использует текущий пакет).
S [[!]pat] Список имен подпрограмм, [не] соответствующих шаблону
V [Pk [Vars]] Список переменных в пакете. Переменные могут быть ~шаблон или !шаблон. (соответствовать шаблону или нет)
X [Vars] То же, что и "V текущий_ пакет (current_package) [Vars]".
y [n [Vars]] Список лексических переменных в более высокой области <n>. Vars такое же как V.
Для получения дополнительных сведений введите h cmd_letter, или запустить man perldebug (perldoc perldebug) для всей справки.
Много запутанных вариантов, что вы даже можете ударить палкой! Это не так плохо, как выглядит, и очень полезно больше узнать об всем этом и весело тоже!
Есть несколько полезных комманд, которые хорошо знать сразу. Вы же не станете думать, что мы пользуемся любыми библиотеками в данный момент, но 'M' (Module) покажет какие модули в настоящее время загружаются, их номер версии, в то время как 'B <m>' покажет методы, а 'S' (Subroutines) показывают все подпрограммы подпадающие под заданный образец, как показанный ниже. 'V' и 'X' показывают переменные в области нашего пакета и могут быть ограничены шаблоном.
DB<2>S str
dumpvar::stringify
strict::bits
strict::import
strict::unimport
Использование 'X' и двоюродных братьев требует от вас не использовать идентификаторы типов ($@ %), только 'имя':
DB<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', в данный момент мы получили пустое (неопределенное) значение (undefined). Теперь нам нужно перейти к следующему исполняемому оператору с помощью буквы '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',который, кстати, вставляет точку останова 'на одно только время' в заданной строке или sub процедуры:
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'
Это не очень нам поможет - пара 'welcome', но никаких признаков того, что является ключами, а что значениями, это просто дамп массива и, в данном случае, не особенно полезная. Хитростью здесь является использование ссылки на структуру данных:
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.
Нечетное число элементов в хэше назначения в ./data строка 5.
Мы исправить наш скобочный оператор на q: '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
Не очень последовательно! Мы установим точку останова в коде вручную и запустим его в отладчике (debugger), чтобы увидеть, что происходит. Точка останова (breakpoint) — это флаг до которого отладчик будет выполнять команды скрипта без перерыва пока он не достигнет точки останова, он остановит выполнение и предложит подсказку для дальнейшего взаимодействия. В обычном режиме эти команды отладчика полностью игнорируются и они являются безопасными - если их случайно оставить в производственном коде.
my ($in, $out) = ($num, $num);
$DB::single=2; # вставить на 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') {
Затем команда осмотр (view) 'v', чтобы увидеть, где мы находимся:
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 }
И print, чтобы показать, какие значения мы в настоящее время используем:
DB<1> p $deg, $num
f33.3
Мы можем установить другую точку останова на любой строке, начинающейся с двоеточия, мы будем использовать строку 19, так как это то место, где мы вышли из подпрограммы, и мы хотели бы остановиться здесь:
DB<2> b 19
Здесь нет обратной связи от нашей команды, но вы можете увидеть установленные точки останова используя команду списка 'L' (list):
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;
Обратите внимание, что если был вызов подпрограммы между нами и строкой 28, и мы хотели пройти через него b<шаг-за-шагом> (single-step), мы могли бы использовать команду 's', чтобы пропустить функцию нужно использовать 'n', которая выполнит подпрограмму, не заходя в нее внутрь для проверки. В этом случае, мы просто продолжаем выполнение вплоть до строки 28:
DB<4> c 28
main::f2c(temp:28): 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) для a, w, t, T
Действия, наблюдение переменных, трассировка стека и т.д.: в TODO листе.
a
w
t
T
РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
Вы когда-нибудь хотели знать как выглядит regex? Вам понадобится perl, скомпилированный с флагом отладки для этого:
> perl -Dr -e '/^pe(a)*rl$/i'
на самом деле этого не нужно, правильная строка отладки(исправление ошибки, примечание переводчика!):
perl -Mre=debug -e '/^pe(a)*rl$/i'
perl '-Mre qw(debug)' -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.
СОВЕТЫ ПО ВЫВОДУ (OUTPUT TIPS)
Чтобы получить весь вывод из вашего журнала ошибок и не пропустить любые сообщения, включая полезные сообщения, буферизированные операцилнной системой, вставьте эту строку в начале вашего скрипта:
$|=1;
Чтобы видеть конец динамично растущего логфайла (logfile), (из командной строки):
tail -f $error_log
Обертка всех вызовов 'die' в процедуру обработчика, чтобы можно было увидеть, как и как и от кого они вызываются, perlvar содержит еще больше информации:
BEGIN { $SIG{__DIE__} = sub { require Carp; Carp::confess(@_) } }
Различные полезные методы для перенаправление дескрипторов STDOUT и STDERR (filehandles) описаны в perlopentut и perlfaq8.
CGI
Быстрая подсказка для всех этих CGI программистов, которые не могут понять, как прийти к земле, чтобы получить последний 'ждущий ввода' ('waiting for input') запрос, при выполнении их CGI сценариев из командной строки, попробуйте что-то вроде этого:
> perl -d my_cgi.pl -nodebug
Конечно CGI и perlfaq9 расскажут вам больше.
GUIs (ГПИ - графический пользовательский интерфейс)
Интерфейс командной строки тесно интегрирован с расширением emacs и с vi интерфейсом тоже.
Вам не придется делать это все в командной строке, хотя, есть несколько GUI вариантов вне этого. Отличная вещь состоит в том, что вы провести мышью над переменной и дамп ее данных будет отображаться в соответствующем окне, или во всплывающем шаре(подсказке), без утомительного нажимания 'x $varname':-)
В частности, вы можете поохотиться на следующих:
ptkdb основанный на perlTK оболочке, встроенный отладчик
ddd отладчика отображения данных (data display debugger)
PerlDevKit и PerlBuilder специфичные для NT
NB.(более подробная информация по этим и другим было бы желательно).
ИТОГ
Мы увидели, как полезна для нас надлежащая практика кодирования с use strict и -w. Мы можем запустить отладчик perl perl -d scriptname, чтобы увидеть наши данные в отладчике perl с помощью команд p и x. Вы можете пройти через ваш код, установив точки останова командой b и пошагово выполнять код командой s или n,продолжить c (continue) и вернуться из подпрограммы r (return). Довольно интуитивно понятные вещи, когда вы ими попользуетесь.
Конечно есть еще много вещей, о которых стоит знать, это просто царапины на поверхности. Лучший способ узнать больше - это использовать perldoc, чтобы узнать больше о языке, чтобы прочитать он-лайн помощь (perldebug, вероятно, следующее место куда вам нужно пойти) и, конечно, эксперименты.
СМОТРИТЕ ТАКЖЕ
perldebug, perldebguts, perldiag, perlrun, отличная презентация на YAPC RUSSIA 2015 Интерактивная отладка Perl программ, суперполезное видео от Сабо Габора Using the built-in debugger of Perl as REPL
Еще отладчики
perl5db.pl Стандартный perl отладчик, входящий в состав perl
Devel::trepan Новый модульный отладчик
Devel::ebug Мультиинтерфейсный отладчик (Консоль, GUI, Web)
Devel::sdb форк perl5db.pl, с отображением выполняемого кода
Devel::hdb Отладчик с Web интерфейсом (REST и HTML)
Devel::PDB Отладчик c TUI интерфейсом
Devel::ptkdb Отладчик с GUI интерфейсом (Perl/Tk)
Enbugger Включает отладчик или профайлер рантайм. С помощью gdb можно подключится к любом perl процессу без какой-либо модификации программы
Regexp::Debugger Аддский PurePerl модуль для интерактиной отладки регексов
Tie::Watch Полезен для гибкого наблюдения за чтением/записью в переменные
Debug::Fork::Tmux Привязка get_fork_TTY к tmux
АВТОР
Ричард Фоли (Richard Foley) <richard.foley@rfi.net> Copyright (c) 2000
УЧАСТНИКИ
Различные люди внесли полезные предложения и материалы, в частности:
Рональд Дж Кимбол (Ronald J Kimball) <rjk@linguist.dartmouth.edu>
Хьюго ван дер Санден (Hugo van der Sanden) <hv@crypt0.demon.co.uk>
Петер Скотт (Peter Scott) <Peter@PSDT.com>
ПЕРЕВОДЧИКИ
Николай Мишин
<mishin@cpan.org>