Spec-Zone.ru › Perl 5.32

perlfaq8

СОДЕРЖАНИЕ

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

НАЗВАНИЕ

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

ВЕРСИЯ

версия 5.20200523

ОПИСАНИЕ

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

Обратитесь к часто задаваемым вопросам и документации, конкретно относящимся к портированию 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's 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" в 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 с новым статическим расширением).

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

В некоторых операционных системах есть ошибки в ядре, которые делают скрипты 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 бит — фактическое выходное значение).

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 . Чтобы объединить STDERR и STDOUT команды:

$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(), при передаче списка обработка оболочки не выполняется. Дополнительные примеры можно найти в "Безопасные открытия каналов по пайпу" в perlipc.

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

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

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

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

    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(), вероятно, в сочетании с обработчиком сигналов, как описано в "Сигналы" в 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: https://metacpan.org/.

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

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

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

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

Если ваша система поддерживает неблокирующие чтения (большинство Unix-подобных систем поддерживают), вам нужно только использовать флаг 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 сделать это за вас. Используйте диагностический пragma, чтобы преобразовать обычные сообщения 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 должно быть отдельным словом. То есть, 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
pragma lib
use lib "$ENV{HOME}/myown_perllib";
модуль local::lib
use local::lib;

use local::lib "~/myown_perllib";

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

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

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

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

perl -V

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

perldoc -l Encode

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

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

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

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

Copyright (c) 1997-2010 Tom Christiansen, Nathan Torkington и другие авторы, как указано. Все права защищены.

Данная документация распространяется бесплатно; вы можете перераспределять и/или изменять её в соответствии с теми же условиями, что и 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.32.0/perlfaq8

Spec-Zone.ru

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