Spec-Zone.ru › Perl 5.28

perlfaq8

СОДЕРЖАНИЕ

  • НАЗВАНИЕ
  • ВЕРСИЯ
  • ОПИСАНИЕ
    • Как узнать, под какой операционной системой я работаю?
    • Почему exec() не возвращает значение?
    • Как выполнять сложные действия с клавиатурой/экраном/мышью?
    • Как вывести текст в цвете?
    • Как прочитать только один символ с клавиатуры без ожидания нажатия Enter?
    • Как проверить готовность ввода с клавиатуры?
    • Как очистить экран?
    • Как получить размер экрана?
    • Как запросить у пользователя пароль?
    • Как читать и записывать в последовательный порт?
    • Как декодировать зашифрованные файлы паролей?
    • Как запустить процесс в фоновом режиме?
    • Как перехватить управляющие символы/сигналы?
    • Как изменить файл паролей shadow на системе Unix?
    • Как установить время и дату?
    • Как выполнить sleep() или alarm() менее чем за секунду?
    • Как измерить время меньше секунды?
    • Как выполнить atexit() или setjmp()/longjmp()? (Обработка исключений)
    • Почему моя программа сокетов не работает под System V (Solaris)? Что означает сообщение об ошибке «Протокол не поддерживается»?
    • Как вызвать уникальные функции C моей системы из Perl?
    • Где получить заголовочные файлы для ioctl() или syscall()?
    • Почему скрипты setuid perl жалуются на проблемы ядра?
    • Как открыть канал связи (pipe) как для ввода, так и для вывода команды?
    • Почему я не могу получить вывод команды с помощью system()?
    • Как перехватить STDERR внешней команды?
    • Почему open() не возвращает ошибку, когда открытие канала связи (pipe) завершается неудачно?
    • Что не так с использованием обратных кавычек в контексте без значения?
    • Как вызвать обратные кавычки без обработки оболочкой?
    • Почему мой скрипт не может читать со стандартного ввода (STDIN) после того, как я передал ему конец файла (EOF) (^D в Unix, ^Z в MS-DOS)?
    • Как перевести мой скрипт оболочки в perl?
    • Можно ли использовать perl для запуска сеанса telnet или ftp?
    • Как написать expect на Perl?
    • Есть ли способ скрыть командную строку perl от программ, таких как «ps»?
    • Я изменил директорию/модифицировал среду в скрипте perl. Почему изменения пропали при выходе из скрипта? Как сделать, чтобы изменения были видны?
    • Как закрыть дескриптор файла процесса без ожидания его завершения?
    • Как создать демонический процесс с помощью fork?
    • Как определить, работаю ли я в интерактивном режиме или нет?
    • Как задать тайм-аут для медленного события?
    • Как установить ограничения на использование процессора?
    • Как избежать создания зомби-процессов на системе Unix?
    • Как использовать базу данных SQL?
    • Как заставить system() завершиться при нажатии Ctrl+C?
    • Как открыть файл без блокировки?
    • Как отличить ошибки оболочки от ошибок perl?
    • Как установить модуль из CPAN?
    • В чем разница между require и use?
    • Как сохранить свой собственный каталог модулей/библиотек?
    • Как добавить директорию, где находится моя программа, в путь поиска модулей/библиотек?
    • Как добавить директорию в путь поиска заголовочных файлов (@INC) во время выполнения?
    • Где устанавливаются модули?
    • Что такое socket.ph и где его получить?
  • АВТОР И АВТОРСКИЕ ПРАВА

НАЗВАНИЕ

perlfaq8 - Взаимодействие с системой

ВЕРСИЯ

версия 5.021011

ОПИСАНИЕ

Этот раздел Perl FAQ посвящен вопросам взаимодействия с операционной системой. Темы включают межпроцессное взаимодействие (IPC), управление пользовательским интерфейсом (клавиатура, экран и указательные устройства) и все, что не связано с обработкой данных.

Прочитайте FAQ и документацию, специфичные для порта perl на вашей операционной системе (например, perlvms, perlplan9, ...). Они должны содержать более подробную информацию о тонкостях вашего perl.

Как узнать, под какой операционной системой я работаю?

Переменная $^O ($OSNAME если вы используете English) содержит указание имени операционной системы (не номер ее версии), для которой был скомпилирован ваш бинарный файл perl.

Почему exec() не возвращает значение?

(внесён brian d foy)

Задача функции exec — превратить ваш процесс в другую команду и никогда не возвращаться. Если это не то, что вам нужно, не используйте exec. :)

Если вы хотите запустить внешнюю команду и сохранить ваш процесс Perl, обратитесь к каналу open, fork, или system.

Как выполнять сложные действия с клавиатурой/экраном/мышью?

Способы доступа к/управлению клавиатурой, экраном и указательными устройствами ("мышами") зависят от системы. Попробуйте следующие модули:

Клавиатура
Term::Cap               Standard perl distribution
Term::ReadKey           CPAN
Term::ReadLine::Gnu     CPAN
Term::ReadLine::Perl    CPAN
Term::Screen            CPAN
Экран
Term::Cap               Standard perl distribution
Curses                  CPAN
Term::ANSIColor         CPAN
Мышь
Tk                      CPAN
Wx                      CPAN
Gtk2                    CPAN
Qt4                     kdebindings4 package

Некоторые из этих частных случаев показаны в качестве примеров в других ответах в этом разделе perlfaq.

Как вывести текст в цвете?

В общем случае нельзя, потому что вы не знаете, имеет ли получатель устройство с цветным дисплеем. Если вы знаете, что получатель имеет ANSI-терминал, понимающий цвета, вы можете использовать модуль Term::ANSIColor из CPAN:

use Term::ANSIColor;
print color("red"), "Stop!\n", color("reset");
print color("green"), "Go!\n", color("reset");

Или так:

use Term::ANSIColor qw(:constants);
print RED, "Stop!\n", RESET;
print GREEN, "Go!\n", RESET;

Как прочитать только один символ с клавиатуры без ожидания нажатия Enter?

Управление буферизацией ввода — чрезвычайно зависимая от системы задача. На многих системах вы можете просто использовать команду stty, как показано в "getc" в perlfunc, но как вы видите, это уже приводит к проблемам с портативностью.

open(TTY, "+</dev/tty") or die "no tty: $!";
system "stty  cbreak </dev/tty >/dev/tty 2>&1";
$key = getc(TTY);        # perhaps this works
# OR ELSE
sysread(TTY, $key, 1);    # probably this does
system "stty -cbreak </dev/tty >/dev/tty 2>&1";

Модуль Term::ReadKey из CPAN предлагает простой в использовании интерфейс, который должен быть эффективнее, чем обращение к stty для каждого символа. Он даже включает ограниченную поддержку Windows.

use Term::ReadKey;
ReadMode('cbreak');
$key = ReadKey(0);
ReadMode('normal');

Однако, для использования кода вам нужен работающий компилятор C и возможность использования его для компиляции и установки модуля CPAN. Вот решение с использованием стандартного модуля POSIX, который уже есть на вашей системе (если ваша система поддерживает POSIX).

use HotKey;
$key = readkey();

И вот модуль HotKey, который скрывает несколько загадочные вызовы для манипулирования структурами POSIX termios.

# HotKey.pm
package HotKey;

use strict;
use warnings;

use parent 'Exporter';
our @EXPORT = qw(cbreak cooked readkey);

use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);

$fd_stdin = fileno(STDIN);
$term     = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm     = $term->getlflag();

$echo     = ECHO | ECHOK | ICANON;
$noecho   = $oterm & ~$echo;

sub cbreak {
    $term->setlflag($noecho);  # ok, so i don't want echo either
    $term->setcc(VTIME, 1);
    $term->setattr($fd_stdin, TCSANOW);
}

sub cooked {
    $term->setlflag($oterm);
    $term->setcc(VTIME, 0);
    $term->setattr($fd_stdin, TCSANOW);
}

sub readkey {
    my $key = '';
    cbreak();
    sysread(STDIN, $key, 1);
    cooked();
    return $key;
}

END { cooked() }

1;

Как проверить, готов ли ввод с клавиатуры?

Самый простой способ сделать это — прочитать символ в режиме без блокировки с помощью модуля Term::ReadKey из CPAN, передав ему аргумент -1, чтобы указать, что не нужно блокировать:

use Term::ReadKey;

ReadMode('cbreak');

if (defined (my $char = ReadKey(-1)) ) {
    # input was waiting and it was $char
} else {
    # no input was waiting
}

ReadMode('normal');                  # restore normal tty settings

Как очистить экран?

(предложено brian d foy)

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

Вы можете использовать модуль Term::ANSIScreen для получения специальной последовательности. Импортируйте функцию cls (или тег :screen) :

use Term::ANSIScreen qw(cls);
my $clear_screen = cls();

print $clear_screen;

Модуль Term::Cap также может получить специальную последовательность, если вы хотите работать с низкоуровневыми деталями управления терминалом. Метод Tputs возвращает строку для данного параметра:

use Term::Cap;

my $terminal = Term::Cap->Tgetent( { OSPEED => 9600 } );
my $clear_screen = $terminal->Tputs('cl');

print $clear_screen;

В Windows вы можете использовать модуль Win32::Console. После создания объекта для файла-обработчика вывода, который вы хотите повлиять, вызовите метод Cls:

Win32::Console;

my $OUT = Win32::Console->new(STD_OUTPUT_HANDLE);
my $clear_string = $OUT->Cls;

print $clear_screen;

Если у вас есть командная строка программы, которая выполняет задачу, вы можете вызвать её в обратных кавычках, чтобы захватить всё, что она выведет, и использовать это позже:

my $clear_string = `clear`;

print $clear_string;

Как получить размер экрана?

Если у вас установлен модуль Term::ReadKey из CPAN, вы можете использовать его для получения ширины и высоты в символах и пикселях:

use Term::ReadKey;
my ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();

Это более портативно, чем исходный ioctl, но не так наглядно:

require './sys/ioctl.ph';
die "no TIOCGWINSZ " unless defined &TIOCGWINSZ;
open(my $tty_fh, "+</dev/tty")                     or die "No tty: $!";
unless (ioctl($tty_fh, &TIOCGWINSZ, $winsize='')) {
    die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ;
}
my ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
print "(row,col) = ($row,$col)";
print "  (xpixel,ypixel) = ($xpixel,$ypixel)" if $xpixel || $ypixel;
print "\n";

Как попросить пользователя ввести пароль?

(Этот вопрос не имеет отношения к веб-технологиям. Обратитесь к другому разделу FAQ для этого.)

Пример этого есть в "crypt" в perlfunc). Сначала вы переводите терминал в режим «без эха», а затем просто читаете пароль в обычном режиме. Вы можете сделать это с помощью функции старого стиля ioctl(), управления терминалом POSIX (см. POSIX или его документацию в Camel Book) или вызовом программы stty, с различной степенью портативности.

Вы также можете сделать это для большинства систем, используя модуль Term::ReadKey из CPAN, который проще в использовании и теоретически более портативный.

use Term::ReadKey;

ReadMode('noecho');
my $password = ReadLine(0);

Как читать и писать в последовательный порт?

Это зависит от операционной системы, на которой работает ваша программа. В случае Unix последовательные порты будут доступны через файлы в /dev; в других системах имена устройств, несомненно, будут отличаться. Несколько проблемных областей, общих для всех взаимодействий с устройствами, следующие:

файлы-блокировки

Ваша система может использовать файлы-блокировки для управления множественным доступом. Убедитесь, что вы следуете правильному протоколу. Непредсказуемое поведение может возникнуть при чтении из одного устройства несколькими процессами.

режим открытия

Если вы ожидаете использовать как операции чтения, так и записи в устройстве, вам нужно открыть его для обновления (подробнее см. "open" в perlfunc). Вы можете открыть его, не рискуя блокировкой, используя sysopen() и O_RDWR|O_NDELAY|O_NOCTTY из модуля Fcntl (часть стандартной дистрибуции Perl). Подробнее об этом подходе см. "sysopen" в perlfunc.

конец строки

Некоторые устройства ожидают «\r» в конце каждой строки, а не «\n». В некоторых портах Perl «\r» и «\n» отличаются от их обычных (Unix) ASCII-значений «\015» и «\012». Возможно, вам придется указывать нужные числовые значения напрямую, используя восьмеричное представление («\015»), шестнадцатеричное («0x0D») или как спецификацию управляющего символа («\cM»).

print DEV "atv1\012";    # wrong, for some devices
print DEV "atv1\015";    # right, for some devices

Хотя в обычных текстовых файлах «\n» выполнит задачу, всё ещё нет единого способа завершения строки, который был бы портативным между Unix, DOS/Win и Macintosh, за исключением завершения ВСЕХ концов строк «\015\012», и удаления ненужного из вывода. Это особенно относится к вводу-выводу через сокеты и автоматическому сбросу, которые обсуждаются ниже.

сброс вывода

Если вы ожидаете, что символы попадут на устройство, когда вы print() их, вам нужно будет сделать этот файл-обработчик автоматического сброса. Вы можете использовать select() и переменную $| для управления автоматическим сбросом (см. "$|" в perlvar и "select" в perlfunc, или perlfaq5, «Как сбросить/отбуферизовать файл-обработчик вывода? Почему это нужно делать?»):

my $old_handle = select($dev_fh);
$| = 1;
select($old_handle);

Вы также увидите код, который делает это без временной переменной, как в

select((select($deb_handle), $| = 1)[0]);

Или, если вам не жалко добавить несколько тысяч строк кода только потому, что вы боитесь небольшой $| переменной:

use IO::Handle;
$dev_fh->autoflush(1);

Как упоминалось в предыдущем пункте, это всё ещё не работает при использовании ввода-вывода через сокеты между Unix и Macintosh. В этом случае вам нужно будет жёстко закодировать ваши разделители строк.

ввод без блокировки

Если вы делаете блокирующий read() или sysread(), вам нужно будет организовать обработчик сигнала тревоги для обеспечения таймаута (см. "alarm" в perlfunc). Если у вас открытие без блокировки, вероятно, вы получите чтение без блокировки, что означает, что вам, возможно, придётся использовать select() с 4 аргументами, чтобы определить, готов ли ввод-вывод на этом устройстве (см. "select" в perlfunc.

Пытаясь прочитать данные из своего прибора для идентификации вызывающего абонента, печально известный Jamie Zawinski <jwz@netscape.com>, после долгих стенаний и борьбы с sysread, sysopen, системой POSIX tcgetattr и различными другими функциями, наконец, придумал это:

sub open_modem {
    use IPC::Open2;
    my $stty = `/bin/stty -g`;
    open2( \*MODEM_IN, \*MODEM_OUT, "cu -l$modem_device -s2400 2>&1");
    # starting cu hoses /dev/tty's stty settings, even when it has
    # been opened on a pipe...
    system("/bin/stty $stty");
    $_ = <MODEM_IN>;
    chomp;
    if ( !m/^Connected/ ) {
        print STDERR "$0: cu printed `$_' instead of `Connected'\n";
    }
}

Как декодировать зашифрованные файлы паролей?

Вы потратили много денег на специализированное оборудование, но это наверняка вас прославит.

Серьёзно, вы не можете, если это файлы паролей Unix — система паролей Unix использует одностороннее шифрование. Это больше похоже на хэширование, чем на шифрование. Лучшее, что вы можете сделать, это проверить, что ещё хэшируется до той же строки. Вы не можете преобразовать хэш обратно в исходную строку. Программы, подобные Crack, могут насильно (и разумно) пытаться угадать пароли, но не гарантируют быстрой удачи.

Если вы обеспокоены выбором плохих паролей пользователями, вы должны активно проверять их, когда они пытаются изменить свой пароль (например, изменив passwd(1)).

Как запустить процесс в фоновом режиме?

(предложено brian d foy)

Нет единого способа запустить код в фоновом режиме, чтобы ваша программа не должна была ждать его завершения, прежде чем переходить к другим задачам. Управление процессами зависит от вашей конкретной операционной системы, и многие из этих техник описаны в perlipc.

Несколько модулей CPAN могут помочь, включая IPC::Open2 или IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background и Win32::Process. Есть много других модулей, которые вы можете использовать, поэтому проверьте и другие пространства имён.

Если вы находитесь в системе наподобие Unix, вы можете обойтись вызовом системы, где вы добавляете & в конец команды:

system("cmd &")

Вы также можете попробовать использовать fork, как описано в perlfunc (хотя это то же самое, что и делают многие модули).

STDIN, STDOUT и STDERR объединены

И основной процесс, и запущенный в фоновом режиме (процесс «дочерний») используют одни и те же файловые обработчики STDIN, STDOUT и STDERR. Если оба пытаются получить к ним доступ одновременно, могут произойти странные вещи. Вы можете закрыть или переопределить эти обработчики для дочернего процесса. Вы можете обойти это с помощью open трубы (см. "open" в perlfunc), но на некоторых системах это означает, что дочерний процесс не может пережить родительский.

Сигналы

Вам нужно будет перехватывать сигнал SIGCHLD, а возможно, и SIGPIPE. SIGCHLD отправляется, когда фоновый процесс завершается. SIGPIPE отправляется, когда вы записываете в файловый обработчик, дочерний процесс которого закрылся (необработанный SIGPIPE может привести к тому, что ваша программа молча завершится). Это не проблема с system("cmd&").

Процессы-зомби

Вы должны быть готовы «собрать» дочерний процесс после его завершения.

$SIG{CHLD} = sub { wait };

$SIG{CHLD} = 'IGNORE';

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

unless ($pid = fork) {
    unless (fork) {
        exec "what you really wanna do";
        die "exec failed!";
    }
    exit 0;
}
waitpid($pid, 0);

См. "Сигналы" в perlipc для других примеров кода, делающего это. Процессы-зомби не являются проблемой с system("prog &").

Как перехватить управляющие символы/сигналы?

Вы не можете фактически «перехватить» управляющий символ. Вместо этого этот символ генерирует сигнал, который отправляется в текущую фоновую группу процесса вашего терминала, которую вы затем перехватываете в своём процессе. Сигналы задокументированы в "Сигналы" в perlipc и разделе «Сигналы» в Camel.

Вы можете установить значения хэша %SIG на функции, которые вы хотите использовать для обработки сигнала. После того, как Perl перехватит сигнал, он ищет в %SIG ключ с тем же именем, что и сигнал, а затем вызывает подпрограмму-значение для этого ключа.

# as an anonymous subroutine

$SIG{INT} = sub { syswrite(STDERR, "ouch\n", 5 ) };

# or a reference to a function

$SIG{INT} = \&ouch;

# or the name of the function as a string

$SIG{INT} = "ouch";

В версиях Perl до 5.8 в исходном коде на C были обработчики сигналов, которые перехватывали сигнал и, возможно, выполняли Perl-функцию, которую вы задали в %SIG. Это нарушало правила обработки сигналов на этом уровне, заставляя Perl выводить ядро. С версии 5.8.0 Perl проверяет %SIG после перехвата сигнала, а не во время перехвата. Предыдущие версии этого ответа были неверны.

Как изменить файл паролей shadow в Unix-системе?

Если Perl был установлен правильно, а ваша библиотека теней была написана должным образом, функции getpw*(), описанные в perlfunc, в теории должны обеспечить (только чтение) доступ к записям в файле паролей теней. Чтобы изменить файл, создайте новый файл паролей теней (формат зависит от системы — см. passwd(1) для подробностей) и используйте pwd_mkdb(8), чтобы установить его (см. pwd_mkdb(8) для более подробной информации).

Как установить время и дату?

Предполагая, что у вас достаточно прав, вы должны иметь возможность установить системную дату и время, выполнив программу date(1). (Нет способа установить время и дату на уровне каждого процесса.) Этот механизм будет работать для Unix, MS-DOS, Windows и NT; эквивалент для VMS — set time.

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

$ENV{TZ} = "MST7MDT";           # Unixish
$ENV{'SYS$TIMEZONE_DIFFERENTIAL'}="-5" # vms
system('trn', 'comp.lang.perl.misc');

Как можно использовать sleep() или alarm() менее чем за секунду?

Если вам нужна более высокая точность, чем 1 секунда, предоставляемая функцией sleep(), самый простой способ — использовать функцию select(), как описано в "select" в perlfunc. Попробуйте модули Time::HiRes и BSD::Itimer (доступны из CPAN, а начиная с Perl 5.8, Time::HiRes входит в стандартное распределение).

Как измерить время менее чем за секунду?

(предоставлено brian d foy)

Модуль Time::HiRes (входит в стандартное распределение с Perl 5.8) измеряет время с помощью системного вызова gettimeofday(), который возвращает время в микросекундах с момента эпохи. Если вы не можете установить Time::HiRes для более старых версий Perl и находитесь в системе Unix-подобной системе, вы можете вызвать gettimeofday(2) напрямую. См. "syscall" в perlfunc.

Как выполнить atexit() или setjmp()/longjmp()? (Обработка исключений)

Вы можете использовать блок END для имитации atexit(). Блок END каждого пакета вызывается при завершении программы или потока. См. страницу руководства perlmod для получения более подробной информации о блоках END.

Например, вы можете использовать это, чтобы убедиться, что ваша программа-фильтр завершила вывод без заполнения диска:

END {
    close(STDOUT) || die "stdout close failed: $!";
}

Блок END не вызывается, когда неулавливаемые сигналы убивают программу, поэтому, если вы используете блоки END, вы также должны использовать

use sigtrap qw(die normal-signals);

Механизм обработки исключений Perl — это оператор eval(). Вы можете использовать eval() в качестве setjmp и die() в качестве longjmp. Подробности об этом см. в разделе о сигналах, особенно в обработчике таймаута для блокирующего flock() в "Signals" в perlipc или в разделе "Сигналы" в Programming Perl.

Если вас интересует только обработка исключений, используйте один из многих модулей CPAN, которые обрабатывают исключения, например, Try::Tiny.

Если вам нужна синтаксическая конструкция atexit() (и rmexit() тоже), попробуйте модуль AtExit из CPAN.

Почему моя программа сокетов не работает в System V (Solaris)? Что означает сообщение об ошибке "Протокол не поддерживается"?

Некоторые системы на основе Sys-V, в частности Solaris 2.X, переопределили некоторые стандартные константы сокетов. Поскольку эти константы были одинаковыми для всех архитектур, они часто жестко закодированы в коде Perl. Правильный подход — использовать "use Socket", чтобы получить правильные значения.

Обратите внимание, что даже несмотря на двоичную совместимость SunOS и Solaris, эти значения отличаются. Непонятно.

Как вызвать уникальные функции C моей системы из Perl?

В большинстве случаев вы пишете внешний модуль для этого — см. ответ на вопрос "Где я могу узнать о связывании C с Perl? [h2xs, xsubpp]". Однако, если функция является системным вызовом, и ваша система поддерживает syscall(), вы можете использовать функцию syscall (описанную в perlfunc).

Не забудьте проверить модули, которые пришли с вашим дистрибутивом, а также CPAN — кто-то, возможно, уже написал модуль для этого. В Windows попробуйте Win32::API. В macOS попробуйте Mac::Carbon. Если модуль не имеет интерфейса к функции C, вы можете встроить немного C в свой исходный код Perl с помощью Inline::C.

Где получить файлы заголовков для ioctl() или syscall()?

Исторически эти файлы генерировались инструментом h2ph, входящим в стандартное распределение Perl. Эта программа преобразует cpp(1) директивы в файлах заголовков C в файлы, содержащие определения подпрограмм, такие как SYS_getitimer(), которые вы можете использовать в качестве аргументов для своих функций. Она не работает идеально, но обычно выполняет большую часть работы. Простые файлы, такие как errno.h, syscall.h и socket.h, подходили, но сложные, такие как ioctl.h, почти всегда требуют ручного редактирования. Вот как установить файлы *.ph:

1. Become the super-user
2. cd /usr/include
3. h2ph *.h */*.h

Если ваша система поддерживает динамическую загрузку, по соображениям портативности и здравого смысла вы, вероятно, должны использовать h2xs (также входит в стандартное распределение Perl). Этот инструмент преобразует файлы заголовков C в расширения Perl. См. perlxstut, чтобы узнать, как начать работу с h2xs.

Если ваша система не поддерживает динамическую загрузку, вы все равно, вероятно, должны использовать h2xs. См. perlxstut и ExtUtils::MakeMaker для получения дополнительной информации (кратко, просто используйте make perl вместо просто make, чтобы перестроить Perl с новым статическим расширением).

Почему скрипты setuid Perl жалуются на проблемы с ядром?

В некоторых операционных системах есть ошибки в ядре, которые делают скрипты setuid потенциально небезопасными. Perl предоставляет вам несколько вариантов (описанных в perlsec), чтобы обойти такие системы.

Как открыть канал связи и ввода/вывода с командой?

Модуль IPC::Open2 (входит в стандартное распределение Perl) — простой в использовании подход, который в своей работе использует pipe(), fork(), и exec(). Однако обязательно прочтите предупреждения о тупиках в его документации (см. IPC::Open2). См. "Взаимодействие с другим процессом" в perlipc и "Взаимодействие с самим собой" в perlipc

Вы также можете использовать модуль IPC::Open3 (входит в стандартное распределение Perl), но имейте в виду, что порядок аргументов у него отличается от IPC::Open2 (см. IPC::Open3).

Почему я не могу получить вывод команды с помощью system()?

Вы путаете назначение system() и обратных кавычек (``). system() выполняет команду и возвращает информацию об её статусе завершения (как 16-битное значение: 7 младших битов — сигнал, по которому процесс завершился, если таковой есть, и 8 старших битов — фактическое значение выхода). Обратные кавычки (``) выполняют команду и возвращают то, что она отправила в STDOUT.

my $exit_status   = system("mail-users");
my $output_string = `ls`;

Как можно получить STDERR от внешней команды?

Существует три основных способа выполнения внешних команд:

system $cmd;        # using system()
my $output = `$cmd`;        # using backticks (``)
open (my $pipe_fh, "$cmd |");    # using open()

С помощью system(), и STDOUT, и STDERR попадут в то же место, что и STDOUT и STDERR скрипта, если команда system() не перенаправит их. Обратные кавычки и open() считывают только STDOUT вашей команды.

Вы также можете использовать функцию open3() из IPC::Open3. Benjamin Goldberg предоставляет некоторые примеры кода:

Для получения STDOUT программы, но игнорирования STDERR:

use IPC::Open3;
use File::Spec;
my $in = '';
open(NULL, ">", File::Spec->devnull);
my $pid = open3($in, \*PH, ">&NULL", "cmd");
while( <PH> ) { }
waitpid($pid, 0);

Для получения STDERR программы, но игнорирования STDOUT:

use IPC::Open3;
use File::Spec;
my $in = '';
open(NULL, ">", File::Spec->devnull);
my $pid = open3($in, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

Для получения STDERR программы и вывода STDOUT в наш собственный STDERR:

use IPC::Open3;
my $in = '';
my $pid = open3($in, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

Для получения и STDOUT, и STDERR команды отдельно, можно перенаправить их в временные файлы, выполнить команду, а затем считать из временных файлов:

use IPC::Open3;
use IO::File;
my $in = '';
local *CATCHOUT = IO::File->new_tmpfile;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&CATCHOUT", ">&CATCHERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
while( <CATCHOUT> ) {}
while( <CATCHERR> ) {}

Но нет необходимости, чтобы оба были временными файлами... следующее должно работать так же, без блокировки:

use IPC::Open3;
my $in = '';
use IO::File;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3($in, \*CATCHOUT, ">&CATCHERR", "cmd");
while( <CATCHOUT> ) {}
waitpid($pid, 0);
seek CATCHERR, 0, 0;
while( <CATCHERR> ) {}

И это будет быстрее, так как мы можем начать обработку stdout программы сразу, а не ждать завершения программы.

В любом из этих примеров вы можете изменить дескрипторы файлов до вызова:

open(STDOUT, ">logfile");
system("ls");

или вы можете использовать перенаправление дескрипторов файлов оболочки Bourne:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");

Вы также можете использовать перенаправление дескрипторов файлов для того, чтобы STDERR стал дубликатом STDOUT:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");

Обратите внимание, что вы не можете просто открыть STDERR как дубликат STDOUT в своей программе Perl и избежать вызова оболочки для перенаправления. Это не работает:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes

Это не работает, потому что open() заставляет STDERR перейти туда, куда был направлен STDOUT в момент вызова open(). Обратные кавычки затем направляют STDOUT в строку, но не изменяют STDERR (он по-прежнему идёт в старый STDOUT).

Обратите внимание, что вы обязательно должны использовать синтаксис перенаправления оболочки Bourne (sh(1)) в обратных кавычках, а не csh(1)! Подробности о том, почему открытия Perl'овские system() и обратные кавычки и каналы связи используют оболочку Bourne, находятся в статье versus/csh.whynot в коллекции "Far More Than You Ever Wanted To Know" в http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . Для получения STDOUT и STDERR команды вместе:

$output = `cmd 2>&1`;                       # either with backticks
$pid = open(PH, "cmd 2>&1 |");              # or with an open pipe
while (<PH>) { }                            #    plus a read

Для получения STDOUT команды, но игнорирования STDERR:

$output = `cmd 2>/dev/null`;                # either with backticks
$pid = open(PH, "cmd 2>/dev/null |");       # or with an open pipe
while (<PH>) { }                            #    plus a read

Для получения STDERR команды, но игнорирования STDOUT:

$output = `cmd 2>&1 1>/dev/null`;           # either with backticks
$pid = open(PH, "cmd 2>&1 1>/dev/null |");  # or with an open pipe
while (<PH>) { }                            #    plus a read

Для обмена STDOUT и STDERR команды, чтобы получить STDERR, но оставить STDOUT в нашем старом STDERR:

$output = `cmd 3>&1 1>&2 2>&3 3>&-`;        # either with backticks
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# or with an open pipe
while (<PH>) { }                            #    plus a read

Для получения и STDOUT, и STDERR команды отдельно, проще всего перенаправить их в отдельные файлы, а затем считать из этих файлов, когда программа завершится:

system("program args 1>program.stdout 2>program.stderr");

Порядок важен во всех этих примерах. Это связано с тем, что оболочка обрабатывает перенаправления дескрипторов файлов строго слева направо.

system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");

Первая команда отправляет стандартный вывод и стандартную ошибку в временный файл. Вторая команда отправляет только старый стандартный вывод в этот файл, а старая стандартная ошибка отображается в старом стандартном выводе.

Почему open() не возвращает ошибку при неудачном открытии канала?

Если второй аргумент к каналам open() содержит метасимволы оболочки, Perl fork() их обрабатывает, то exec() оболочку для декодирования метасимволов и, в конечном итоге, запуска нужной программы. Если программа не может быть запущена, сообщение об ошибке получает оболочка, а не Perl. Ваш Perl-программа может узнать только, смогла ли оболочка успешно запуститься. Вы все еще можете получить STDERR оболочки и проверить его на наличие сообщений об ошибках. См. "Как можно получить STDERR от внешней команды?" в этом документе или использовать модуль IPC::Open3.

Если в аргументе open() нет метасимволов оболочки, Perl запускает команду напрямую, без использования оболочки, и может правильно сообщить, запустилась ли команда.

Что не так с использованием обратных кавычек в контексте void?

Строго говоря, ничего. С точки зрения стиля, это не лучший способ написания поддерживаемого кода. Perl имеет несколько операторов для запуска внешних команд. Обратные кавычки — один из них; они собирают вывод команды для использования в вашей программе. Функция system — другой вариант; она этого не делает.

Написание обратных кавычек в вашей программе ясно сообщает читателям вашего кода, что вы хотите собрать вывод команды. Зачем отправлять ясное сообщение, которое не соответствует действительности?

Рассмотрим эту строку:

`cat /etc/termcap`;

Вы забыли проверить $? , чтобы убедиться, что программа вообще запустилась правильно. Даже если вы написали

print `cat /etc/termcap`;

Этот код можно и, вероятно, нужно переписать как

system("cat /etc/termcap") == 0
or die "cat program failed!";

что выведет вывод команды cat по мере его генерации, вместо того, чтобы ждать завершения программы для его вывода. Также проверяется возвращаемое значение.

system также предоставляет прямой контроль над тем, может ли происходить обработка символов подстановки оболочки, тогда как обратные кавычки этого не делают.

Как вызвать обратные кавычки без обработки оболочкой?

Это немного сложно. Вы не можете просто написать команду так:

@ok = `grep @opts '$search_string' @filenames`;

Начиная с Perl 5.8.0, вы можете использовать open() с несколькими аргументами. Так же, как и списковые формы system() и exec(), никаких обращений к оболочке не происходит.

open( GREP, "-|", 'grep', @opts, $search_string, @filenames );
chomp(@ok = <GREP>);
close GREP;

Вы также можете:

my @ok = ();
if (open(GREP, "-|")) {
    while (<GREP>) {
        chomp;
        push(@ok, $_);
    }
    close GREP;
} else {
    exec 'grep', @opts, $search_string, @filenames;
}

Так же, как и с system(), никаких обращений к оболочке не происходит, когда вы exec() список. Более подробные примеры можно найти в "Safe Pipe Opens" in perlipc.

Обратите внимание, что если вы используете Windows, никакого решения этой проблемы нет. Несмотря на то, что Perl эмулирует fork(), вы все равно окажетесь в тупике, так как Windows не имеет API в стиле argc/argv.

Почему мой скрипт не может читать из STDIN после того, как я передал ему EOF (^D в Unix, ^Z в MS-DOS)?

Это происходит только в том случае, если ваш Perl скомпилирован для использования stdio вместо perlio, что является стандартным вариантом. Некоторые (возможно, все?) stdio устанавливают флаги ошибок и eof, которые вам может потребоваться очистить. Модуль POSIX определяет clearerr(), который вы можете использовать. Это технически правильный способ сделать это. Вот некоторые менее надежные обходные пути:

  1. Попробуйте сохранить указатель поиска и перейти к нему, как в этом примере:

    my $where = tell($log_fh);
    seek($log_fh, $where, 0);
  2. Если это не сработает, попробуйте перейти к другой части файла, а затем вернуться.

  3. Если это не сработает, попробуйте перейти к другой части файла, прочитать что-то и вернуться назад.

  4. Если это не сработает, откажитесь от вашего пакета stdio и используйте sysread.

Как преобразовать мой скрипт оболочки в Perl?

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

Можно ли использовать Perl для запуска сеанса telnet или ftp?

Попробуйте модули Net::FTP, TCP::Client и Net::Telnet (доступны из CPAN). http://www.cpan.org/scripts/netstuff/telnet.emul.shar также поможет для эмуляции протокола telnet, но Net::Telnet гораздо проще в использовании.

Если все, что вам нужно, это имитация telnet, но вам не нужен начальный обмен данными telnet, то стандартный подход с двумя процессами будет достаточным:

use IO::Socket;             # new in 5.004
my $handle = IO::Socket::INET->new('www.perl.com:80')
    or die "can't connect to port 80 on www.perl.com $!";
$handle->autoflush(1);
if (fork()) {               # XXX: undef means failure
    select($handle);
    print while <STDIN>;    # everything from stdin to socket
} else {
    print while <$handle>;  # everything from socket to stdout
}
close $handle;
exit;

Как написать Expect на Perl?

Когда-то существовала библиотека под названием chat2.pl (часть стандартной дистрибуции Perl), которая так и не была завершена. Если вы найдете ее где-то, не используйте ее. В наши дни ваш лучший вариант — посмотреть на модуль Expect, доступный из CPAN, который также требует двух других модулей из CPAN, IO::Pty и IO::Stty.

Существует ли способ скрыть командную строку Perl от программ, таких как «ps»?

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

Чтобы фактически изменить видимую командную строку, можно присвоить значение переменной $0, как описано в perlvar. Однако это не будет работать на всех операционных системах. Программы-демоны, такие как sendmail, размещают там свои данные состояния, например:

$0 = "orcus [accepting connections]";

Я {изменил каталог, изменил среду} в скрипте Perl. Почему изменения исчезли при выходе из скрипта? Как сделать изменения видимыми?

Unix

В строгом смысле этого сделать нельзя — скрипт выполняется как отдельный процесс от оболочки, из которой он был запущен. Изменения в процессе не отражаются в его родительском процессе — только в любых дочерних процессах, созданных после изменения. Существует магическая функция оболочки, которая может позволить вам смоделировать это, eval() вывод скрипта в вашей оболочке; обратите внимание на FAQ в comp.unix.questions для получения подробной информации.

Как закрыть дескриптор файла процесса без ожидания его завершения?

Предполагая, что ваша система поддерживает такие действия, просто отправьте соответствующий сигнал процессу (см. "kill" в perlfunc). Обычно сначала отправляется сигнал TERM, ожидается некоторое время, а затем отправляется сигнал KILL для завершения.

Как создать демонизированный процесс?

Если под демонизированным процессом вы подразумеваете процесс, отделенный (отключенный от своего tty), то следующий процесс, как сообщается, работает на большинстве Unix-систем. Пользователям, не работающим с Unix, следует проверить модуль Your_OS::Process для других решений.

  • Открыть /dev/tty и использовать ioctl TIOCNOTTY на нем. См. tty(1) для получения подробной информации. Или, что еще лучше, можно просто использовать функцию POSIX::setsid(), чтобы не беспокоиться о группах процессов.

  • Изменить каталог на /

  • Перезапустить STDIN, STDOUT и STDERR, чтобы они не были подключены к старому tty.

  • Запустить себя в фоновом режиме так:

    fork && exit;

Модуль Proc::Daemon, доступный из CPAN, предоставляет функцию для выполнения этих действий за вас.

Как узнать, выполняюсь ли я в интерактивном режиме или нет?

(предоставлено brian d foy)

Это сложный вопрос, и лучший ответ — только предположение.

Что вы на самом деле хотите узнать? Если вы просто хотите узнать, подключен ли один из ваших дескрипторов файла к терминалу, вы можете попробовать -t тесте файла:

if( -t STDOUT ) {
    print "I'm connected to a terminal!\n";
}

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

Модуль IO::Interactive делает все возможное, чтобы дать вам ответ. Его функция is_interactive возвращает дескриптор выходного файла; этот дескриптор указывает на стандартный вывод, если модуль считает, что сеанс интерактивный. В противном случае дескриптор файла — пустой дескриптор, который просто отбрасывает вывод:

use IO::Interactive;

print { is_interactive } "I might go to standard output!\n";

Это по-прежнему не гарантирует, что на ваши запросы отвечает реальный человек или что он читает ваш вывод.

Если вы хотите узнать, как обработать автоматизированное тестирование для вашей дистрибуции, вы можете проверить среду. Например, тестировщики CPAN устанавливают значение AUTOMATED_TESTING:

unless( $ENV{AUTOMATED_TESTING} ) {
    print "Hello interactive tester!\n";
}

Как задать таймаут для медленного события?

Используйте функцию alarm(), вероятно, в сочетании с обработчиком сигналов, как описано в "Signals" in perlipc и разделе "Сигналы" в Camel. Вы можете вместо этого использовать более гибкий модуль Sys::AlarmCall, доступный из CPAN.

Функция alarm() не реализована во всех версиях Windows. Проверьте документацию для вашей конкретной версии Perl.

Как задать ограничения на использование процессора?

(предоставлено Xho)

Используйте модуль BSD::Resource из CPAN. Например:

use BSD::Resource;
setrlimit(RLIMIT_CPU,10,20) or die $!;

Это задает мягкие и жесткие ограничения соответственно на 10 и 20 секунд. После 10 секунд, потраченных на выполнение на процессоре (а не «времени стен»), процессу будет отправлен сигнал (XCPU на некоторых системах), который, если не перехвачен, приведет к завершению процесса. Если этот сигнал перехвачен, то через 10 дополнительных секунд (в общей сложности 20 секунд) процессу будет отправлен непоймаемый сигнал.

См. BSD::Resource и документацию вашей системы для получения подробной информации.

Как избежать зомби-процессов на Unix-системе?

Используйте код сбора данных из "Сигналов" в perlipc, чтобы вызвать wait(), когда получен сигнал SIGCHLD, или используйте технику двойного форка, описанную в "Как запустить процесс на заднем плане?" в perlfaq8.

Как использовать базу данных SQL?

Модуль DBI предоставляет абстрактный интерфейс к большинству серверов и типов баз данных, включая Oracle, DB2, Sybase, mysql, Postgresql, ODBC и плоские файлы. Модуль DBI обращается к каждому типу базы данных через драйвер базы данных или DBD. Полный список доступных драйверов можно посмотреть на CPAN: http://www.cpan.org/modules/by-module/DBD/ . Подробнее о DBI можно прочитать на http://dbi.perl.org/ .

Другие модули обеспечивают более специфический доступ: Win32::ODBC, Alzabo, iodbc, и другие, найденные на CPAN Search: http://search.cpan.org/ .

Как сделать так, чтобы system() завершился при нажатии Ctrl+C?

Нельзя. Вам нужно имитировать вызов system() (см. perlipc для примера кода), а затем создать обработчик сигнала для сигнала INT, который передает сигнал дочернему процессу. Или вы можете проверить его:

$rc = system($cmd);
if ($rc & 127) { die "signal death" }

Как открыть файл без блокировки?

Если у вас есть удача, и ваша система поддерживает неблокирующие чтение (большинство уникси-подобных систем это делают), вам достаточно использовать флаг O_NDELAY или O_NONBLOCK из модуля Fcntl в сочетании с sysopen():

use Fcntl;
sysopen(my $fh, "/foo/somefile", O_WRONLY|O_NDELAY|O_CREAT, 0644)
    or die "can't open /foo/somefile: $!":

Как отличить ошибки оболочки от ошибок Perl?

(ответ предоставлен brian d foy)

Когда вы запускаете скрипт Perl, что-то ещё выполняет этот скрипт за вас, и это что-то ещё может выводить сообщения об ошибках. Скрипт может выдавать собственные предупреждения и сообщения об ошибках. Большую часть времени нельзя определить, кто что сказал.

Вы, вероятно, не сможете исправить то, что запускает Perl, но вы можете изменить способ вывода предупреждений Perl, определив пользовательские функции предупреждений и завершения работы.

Рассмотрим этот скрипт, у которого есть ошибка, которую вы можете не заметить сразу.

#!/usr/locl/bin/perl

print "Hello World\n";

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

$ ./test
./test: line 3: print: command not found

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

#!/usr/bin/perl -w

BEGIN {
    $SIG{__WARN__} = sub{ print STDERR "Perl: ", @_; };
    $SIG{__DIE__}  = sub{ print STDERR "Perl: ", @_; exit 1};
}

$a = 1 + undef;
$x / 0;
__END__

Сообщение Perl выводится с префиксом "Perl". Блок BEGIN работает во время компиляции, поэтому все ошибки и предупреждения компиляции также получают префикс "Perl:".

Perl: Useless use of division (/) in void context at ./test line 9.
Perl: Name "main::a" used only once: possible typo at ./test line 8.
Perl: Name "main::x" used only once: possible typo at ./test line 9.
Perl: Use of uninitialized value in addition (+) at ./test line 8.
Perl: Use of uninitialized value in division (/) at ./test line 9.
Perl: Illegal division by zero at ./test line 9.
Perl: Illegal division by zero at -e line 3.

Если я не вижу "Perl:", это не ошибка Perl.

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

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

use diagnostics;

Если вы не получите абзаца или двух расширенных обсуждений, это, возможно, не сообщение Perl.

Как установить модуль из CPAN?

(сообщение предоставлено brian d foy)

Самый простой способ — использовать команду cpan, которая поставляется с Perl, и пусть модуль CPAN сделает это за вас. Вы можете указать список модулей для установки:

$ cpan IO::Interactive Getopt::Whatever

Если вы предпочитаете CPANPLUS, это также легко:

$ cpanp i IO::Interactive Getopt::Whatever

Если вы хотите установить дистрибутив из текущей директории, вы можете сказать CPAN.pm установить . (точку):

$ cpan .

Обратитесь к документации для этих команд, чтобы узнать, что еще можно сделать.

Если вы хотите попробовать установить дистрибутив самостоятельно, решив все зависимости самостоятельно, вы можете выбрать один из двух возможных путей сборки.

Для дистрибутивов, использующих Makefile.PL:

$ perl Makefile.PL
$ make test install

Для дистрибутивов, использующих Build.PL:

$ perl Build.PL
$ ./Build test
$ ./Build install

Некоторые дистрибутивы могут потребовать подключения к библиотекам или другому стороннему коду, и их последовательности сборки и установки могут быть более сложными. Проверьте любые файлы README или INSTALL, которые вы найдете.

В чем разница между require и use?

(сообщение предоставлено brian d foy)

Perl выполняет оператор require во время выполнения. После того, как Perl загрузит, скомпилирует и выполнит файл, он ничего больше не делает. Оператор use эквивалентен выполнению require во время компиляции, но Perl также вызывает метод import для загруженного пакета. Эти два оператора эквивалентны:

use MODULE qw(import list);

BEGIN {
    require MODULE;
    MODULE->import(import list);
}

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

use MODULE ();

BEGIN {
    require MODULE;
}

Поскольку use также вызовет метод import, фактическое значение для MODULE должно быть именованной константой (bareword). То есть use не может загружать файлы по имени, хотя require может:

require "$ENV{HOME}/lib/Foo.pm"; # no @INC searching!

См. запись для use в perlfunc для получения дополнительной информации.

Как сохранить свой собственный каталог модулей/библиотек?

При создании модулей сообщите Perl, куда установить модули.

Если вы хотите установить модули для собственного использования, самым простым способом может быть local::lib, который можно загрузить с CPAN. Он устанавливает различные параметры установки за вас и использует те же параметры внутри ваших программ.

Если вам нужна большая гибкость, вам нужно настроить свой клиент CPAN для вашей конкретной ситуации.

Для дистрибутивов на основе Makefile.PL, используйте параметр INSTALL_BASE при генерации Makefiles:

perl Makefile.PL INSTALL_BASE=/mydir/perl

Вы можете установить это в своей конфигурации CPAN.pm, чтобы модули автоматически устанавливались в ваш личный каталог библиотеки при использовании оболочки CPAN.pm:

% cpan
cpan> o conf makepl_arg INSTALL_BASE=/mydir/perl
cpan> o conf commit

Для дистрибутивов на основе Build.PL, используйте параметр --install_base:

perl Build.PL --install_base /mydir/perl

Вы также можете настроить CPAN.pm на автоматическое использование этого параметра:

% cpan
cpan> o conf mbuild_arg "--install_base /mydir/perl"
cpan> o conf commit

INSTALL_BASE сообщает этим инструментам разместить ваши модули в /mydir/perl/lib/perl5. См. "Как добавить директорию в путь включения (@INC) во время выполнения?" для получения подробной информации о том, как запустить ваши недавно установленные модули.

Однако есть один нюанс с INSTALL_BASE, так как он работает иначе, чем параметры PREFIX и LIB, которые рекомендовали более старые версии ExtUtils::MakeMaker. INSTALL_BASE не поддерживает установку модулей для нескольких версий Perl или разных архитектур в одном каталоге. Вы должны подумать, действительно ли вам это нужно, и, если да, использовать старые параметры PREFIX и LIB. См. документацию ExtUtils::Makemaker для получения дополнительной информации.

Как добавить директорию, в которой находится моя программа, в путь поиска модулей/библиотек?

(сообщение предоставлено brian d foy)

Если вы уже знаете директорию, вы можете добавить её в @INC, как и любую другую директорию. Вы можете использовать <use lib>, если знаете директорию во время компиляции:

use lib $directory;

Секрет в этой задаче — найти эту директорию. Перед тем, как ваша программа начнёт что-либо делать (например, chdir), вы можете получить текущую рабочую директорию с помощью модуля Cwd, который поставляется с Perl:

BEGIN {
    use Cwd;
    our $directory = cwd;
}

use lib $directory;

Вы можете сделать то же самое со значением $0, которое содержит имя скрипта. Это может содержать относительный путь, но rel2abs может преобразовать его в абсолютный путь. После того, как у вас есть

BEGIN {
    use File::Spec::Functions qw(rel2abs);
    use File::Basename qw(dirname);

    my $path   = rel2abs( $0 );
    our $directory = dirname( $path );
}

use lib $directory;

Модуль FindBin, который поставляется с Perl, может сработать. Он находит директорию текущего выполняемого скрипта и помещает её в $Bin, которую вы можете использовать для построения правильного пути к библиотеке:

use FindBin qw($Bin);

Вы также можете использовать local::lib для выполнения большей части той же работы. Установите модули, используя настройки local::lib, а затем используйте модуль в вашей программе:

use local::lib; # sets up a local lib at ~/perl5

См. документацию local::lib для получения дополнительной информации.

Как добавить директорию в путь включения (@INC) во время выполнения?

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

переменная среды PERLLIB
$ export PERLLIB=/path/to/my/dir
$ perl program.pl
переменная среды PERL5LIB
$ export PERL5LIB=/path/to/my/dir
$ perl program.pl
флаг командной строки perl -Idir
$ perl -I/path/to/my/dir program.pl
прагма lib:
use lib "$ENV{HOME}/myown_perllib";
модуль local::lib:
use local::lib;

use local::lib "~/myown_perllib";

Последний вариант особенно полезен, так как он учитывает архитектуру, зависящую от машины. Модуль прагматики lib.pm был впервые включён в релиз Perl 5.002.

Где устанавливаются модули?

Модули устанавливаются на основе конкретных ситуаций (как описано в предыдущем разделе) и в операционной системе. Все эти пути хранятся в @INC, который можно отобразить с помощью однострочного оператора

perl -e 'print join("\n",@INC,"")'

Та же информация отображается в конце вывода команды

perl -V

Чтобы узнать, где находится исходный код модуля, используйте

perldoc -l Encode

для отображения пути к модулю. В некоторых случаях (например, модуль AutoLoader) эта команда отобразит путь к отдельному файлу pod; сам модуль должен быть в той же директории с расширением 'pm'.

Что такое socket.ph и где его получить?

Это файл в стиле Perl 4, определяющий значения системных сетевых констант. Иногда он создаётся с помощью h2ph при установке Perl, но иногда нет. Современные программы должны использовать use Socket; вместо этого.

АВТОР И АВТОРСКИЕ ПРАВА

Авторские права (c) 1997-2010 Том Кристиансен, Нейтан Торкингтон и другие авторы, как указано. Все права защищены.

Данная документация распространяется свободно; вы можете перераспределять и/или изменять ее на тех же условиях, что и Perl сам по себе.

Независимо от его распространения, все примеры кода в этом файле объявляются общественным достоянием. Вам разрешается и рекомендуется использовать этот код в своих программах для развлечения или с целью получения прибыли, как вам заблагорассудится. Простой комментарий в коде, приписывающий авторство, был бы вежливым, но не является обязательным.

© 1993–2020 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.28.3/perlfaq8

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API