Spec-Zone.ru › Perl 5.30

perlfaq8

СОДЕРЖАНИЕ

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

ИМЯ

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

ВЕРСИЯ

версия 5.20190126

ОПИСАНИЕ

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

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

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

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

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

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

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

Если вы хотите запустить внешнюю команду и сохранить при этом свой процесс Perl, обратитесь к вариантам с использованием канала связи (pipe), 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;

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

Управление буферизацией ввода — чрезвычайно зависимая от системы задача. На многих системах вы можете просто использовать команду 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?

Используйте код сборщика (reaper) из раздела "Сигналы" в 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 сделать это за вас. Используйте диагностический pragma, чтобы преобразовать обычные сообщения 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 при генерации Make файлов:

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";

Последний вариант особенно полезен, потому что он учитывает архитектуру, зависящую от машины. Модуль pragma 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; вместо него.

END_OF_DOCUMENT_MARKER

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

Авторские права (с) 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.30.3/perlfaq8

Spec-Zone.ru

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