perlfaq8
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ОПИСАНИЕ
- Как узнать, под какой операционной системой я работаю?
- Почему exec() не возвращает значение?
- Как выполнять сложные действия с клавиатурой/экраном/мышью?
- Как вывести текст в цвете?
- Как прочитать только один символ с клавиатуры без ожидания нажатия Enter?
- Как проверить, готов ли ввод с клавиатуры?
- Как очистить экран?
- Как получить размер экрана?
- Как запросить у пользователя пароль?
- Как читать и записывать в последовательный порт?
- Как декодировать зашифрованные файлы паролей?
- Как запустить процесс на заднем плане?
- Как перехватить управляющие символы/сигналы?
- Как изменить файл паролей shadow на системе Unix?
- Как установить время и дату?
- Как сделать sleep() или alarm() меньше секунды?
- Как измерить время меньше секунды?
- Как сделать atexit() или setjmp()/longjmp()? (Обработка исключений)
- Почему моя программа сокетов не работает под System V (Solaris)? Что означает сообщение об ошибке «Протокол не поддерживается»?
- Как вызвать уникальные функции C моей системы из Perl?
- Где взять файлы заголовков для ioctl() или syscall()?
- Почему скрипты setuid perl жалуются на проблемы ядра?
- Как открыть канал связи, как для ввода, так и для вывода команды?
- Почему я не могу получить вывод команды с помощью system()?
- Как перехватить STDERR от внешней команды?
- Почему open() не возвращает ошибку, когда открытие канала связи завершается неудачно?
- Что не так с использованием обратных кавычек в контексте void?
- Как вызвать обратные кавычки без обработки оболочки?
- Почему мой скрипт не может читать из STDIN после того, как я дал ему EOF (^D в Unix, ^Z в MS-DOS)?
- Как преобразовать мой скрипт оболочки в perl?
- Можно ли использовать perl для запуска сеанса telnet или ftp?
- Как написать expect на Perl?
- Есть ли способ скрыть командную строку perl от программ, таких как «ps»?
- Я {изменил каталог, изменил свою среду} в скрипте perl. Почему изменения исчезли при выходе из скрипта? Как сделать, чтобы изменения были видны?
- Как закрыть дескриптор файла процесса, не дожидаясь его завершения?
- Как создать демонический процесс с помощью fork?
- Как узнать, выполняюсь ли я интерактивно или нет?
- Как задать таймаут для медленного события?
- Как установить ограничения на использование процессора?
- Как избежать «зомби» на системе Unix?
- Как использовать базу данных SQL?
- Как заставить system() завершиться при нажатии Ctrl+C?
- Как открыть файл без блокировки?
- Как отличить ошибки оболочки от ошибок perl?
- Как установить модуль из CPAN?
- В чём разница между require и use?
- Как сохранить свой собственный каталог модулей/библиотек?
- Как добавить каталог, в котором находится моя программа, в путь поиска модулей/библиотек?
- Как добавить каталог в путь включения (@INC) во время выполнения?
- Где устанавливаются модули?
- Что такое socket.ph и где его получить?
- АВТОР И АВТОРСКИЕ ПРАВА
НАЗВАНИЕ
perlfaq8 - Взаимодействие с системой
ВЕРСИЯ
версия 5.20210520
ОПИСАНИЕ
Этот раздел Perl FAQ посвящён вопросам взаимодействия с операционной системой. Тематика включает межпроцессное взаимодействие (IPC), управление пользовательским интерфейсом (клавиатура, экран и указательные устройства) и всё остальное, что не связано с обработкой данных.
Прочитайте FAQ и документацию, специфичную для вашей операционной системы (например, 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 (или тег %%%CODE_BLOCK_30%%):
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. После создания объекта для требуемого файлового дескриптора вызова вызовите метод %%%CODE_BLOCK_36%%:
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]);Или, если вы не возражаете против включения нескольких тысяч строк кода только потому, что боитесь небольшой переменной %%%CODE_BLOCK_61%%:
use IO::Handle; $dev_fh->autoflush(1);Как отмечалось в предыдущем пункте, это по-прежнему не работает при использовании ввода-вывода сокетов между Unix и Macintosh. В этом случае необходимо жестко закодировать ваши символы завершения строки.
- ввод без блокировки
-
Если вы выполняете блокирующее
read()илиsysread(), вам нужно организовать обработчик сигнала таймаута (см. "alarm" в perlfunc). Если у вас открыт дескриптор без блокировки, скорее всего, чтение тоже будет без блокировки, что означает, что вам, возможно, придется использоватьselect()с 4 аргументами, чтобы определить готовность ввода-вывода на этом устройстве (см. "select" в perlfunc.
Попытки прочитать данные из своего устройства Caller ID, известный Jamie Zawinski <jwz@netscape.com>, после многочисленных скрежета зубов и борьбы с sysread, sysopen, механизмами POSIX tcgetattr и различными другими функциями, которые «ходят» по ночам, в итоге придумал это:
sub open_modem {
use IPC::Open2;
my $stty = `/bin/stty -g`;
open2( \*MODEM_IN, \*MODEM_OUT, "cu -l$modem_device -s2400 2>&1");
# starting cu hoses /dev/tty's stty settings, even when it has
# been opened on a pipe...
system("/bin/stty $stty");
$_ = <MODEM_IN>;
chomp;
if ( !m/^Connected/ ) {
print STDERR "$0: cu printed `$_' instead of `Connected'\n";
}
} Как декодировать зашифрованные файлы паролей?
Вы потратили много денег на специализированное оборудование, но это непременно вызовет разговоры.
Серьёзно, вы не можете, если это файлы паролей Unix — система паролей Unix использует одностороннее шифрование. Это больше похоже на хэширование, чем на шифрование. Лучшее, что вы можете сделать, это проверить, хешируется ли что-то еще до той же строки. Вы не можете преобразовать хэш обратно в исходную строку. Программы вроде Crack могут пытаться угадать пароли силой (и с умом), но не гарантируют быстрого успеха.
Если вы беспокоитесь о том, что пользователи выбирают плохие пароли, вы должны проактивно проверять их при попытке смены пароля (например, модифицируя passwd(1)).
Как запустить процесс в фоновом режиме?
(предложено brian d foy)
Нет единого способа запускать код в фоновом режиме, чтобы ваша программа могла переходить к другим задачам, не дожидаясь его завершения. Управление процессами зависит от конкретной операционной системы, и многие из этих техник описаны в perlipc.
Несколько модулей CPAN могут помочь, в том числе IPC::Open2 или IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background и Win32::Process. Есть и другие модули, которые вы можете использовать, поэтому проверьте эти пространства имён для других вариантов.
Если вы используете систему, похожую на Unix, вы можете обойтись вызовом системы, где вы добавляете & в конец команды:
system("cmd &") Вы также можете попробовать использовать fork, как описано в perlfunc (хотя это то же самое, что делают многие из этих модулей).
-
И основной процесс, и фоновый процесс (процесс «дочерний») совместно используют те же файловые дескрипторы STDIN, STDOUT и STDERR. Если оба попытаются получить к ним доступ одновременно, могут произойти странные вещи. Возможно, вам нужно закрыть или переоткрыть их для дочернего процесса. Вы можете обойти это,
openpipe (см. "open" в perlfunc), но в некоторых системах это означает, что дочерний процесс не может пережить родительский. - Сигналы
-
Вам нужно будет перехватить сигнал SIGCHLD, а возможно, и SIGPIPE. SIGCHLD отправляется, когда завершается фоновый процесс. SIGPIPE отправляется, когда вы записываете в файловый дескриптор, который был закрыт дочерним процессом (неперехваченный SIGPIPE может привести к молчаливому завершению вашей программы). Это не проблема с
system("cmd&"). - Процессы-зомби
-
Вам нужно подготовиться к «сбору» дочернего процесса после его завершения.
$SIG{CHLD} = sub { wait }; $SIG{CHLD} = 'IGNORE';Вы также можете использовать двойное разветвление. Сразу же
wait()для своего первого дочернего процесса, а демон initwait()для вашего внука, как только он выйдет.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 был установлен правильно, и ваша библиотека shadow была написана корректно, функции getpw*(), описанные в perlfunc, в теории должны обеспечивать (только для чтения) доступ к записям в файле паролей shadow. Для изменения файла создайте новый файл паролей shadow (формат различается в зависимости от системы — см. 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)? Что означает сообщение об ошибке "Protocol not supported"?
Некоторые системы на основе Sys-V, в частности Solaris 2.X, переопределили некоторые стандартные константы сокетов. Поскольку они были постоянными для всех архитектур, они часто были жестко запрограммированы в код Perl. Правильный способ решения этой проблемы — использовать "use Socket", чтобы получить правильные значения.
Обратите внимание, что даже если SunOS и Solaris совместимы на бинарном уровне, эти значения отличаются. Как ни странно.
Как вызвать уникальные C-функции моей системы из Perl?
В большинстве случаев для этого пишется внешний модуль — см. ответ на вопрос "Где можно узнать о связывании C с Perl? [h2xs, xsubpp]". Однако, если функция является системным вызовом, и ваша система поддерживает syscall(), вы можете использовать функцию syscall (описанную в perlfunc).
Не забудьте проверить модули, которые поставлялись с вашим дистрибутивом, а также CPAN — возможно, кто-то уже написал модуль для этого. В Windows попробуйте Win32::API. На Mac попробуйте 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 битов — фактическое значение возврата). Обратные кавычки (``) выполняют команду и возвращают то, что она отправила в STDOUT.
my $exit_status = system("mail-users");
my $output_string = `ls`; Как захватить STDERR из внешней команды?
Есть три основных способа запуска внешних команд:
system $cmd; # using system()
my $output = `$cmd`; # using backticks (``)
open (my $pipe_fh, "$cmd |"); # using open() В случае использования system(), как STDOUT, так и STDERR попадут в то же место, что и STDOUT и STDERR скрипта, если команда system() не перенаправит их. Обратные кавычки и open() считывают только STDOUT вашей команды.
Вы также можете использовать функцию open3() из IPC::Open3. Benjamin Goldberg предоставляет примеры кода:
Для захвата STDOUT программы, но отброса STDERR:
use IPC::Open3;
use File::Spec;
my $in = '';
open(NULL, ">", File::Spec->devnull);
my $pid = open3($in, \*PH, ">&NULL", "cmd");
while( <PH> ) { }
waitpid($pid, 0); Для захвата STDERR программы, но отброса STDOUT:
use IPC::Open3;
use File::Spec;
my $in = '';
open(NULL, ">", File::Spec->devnull);
my $pid = open3($in, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0); Для захвата STDERR программы и вывода STDOUT в наш собственный STDERR:
use IPC::Open3;
my $in = '';
my $pid = open3($in, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0); Для чтения как STDOUT, так и STDERR команды по отдельности, можно перенаправить их в временные файлы, дождаться завершения команды, а затем прочитать временные файлы:
use IPC::Open3;
use IO::File;
my $in = '';
local *CATCHOUT = IO::File->new_tmpfile;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&CATCHOUT", ">&CATCHERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
while( <CATCHOUT> ) {}
while( <CATCHERR> ) {} Однако нет реальной необходимости в обоих временных файлах... следующее должно работать так же, без тупиков:
use IPC::Open3;
my $in = '';
use IO::File;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3($in, \*CATCHOUT, ">&CATCHERR", "cmd");
while( <CATCHOUT> ) {}
waitpid($pid, 0);
seek CATCHERR, 0, 0;
while( <CATCHERR> ) {} И это будет быстрее, так как мы можем начать обработку stdout программы сразу же, а не ждать завершения программы.
В любом из этих случаев можно изменить дескрипторы файлов перед вызовом:
open(STDOUT, ">logfile");
system("ls"); или использовать перенаправление дескрипторов файлов оболочки Bourne:
$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |"); Также можно использовать перенаправление дескрипторов файлов для дублирования STDERR в STDOUT:
$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |"); Обратите внимание, что вы не можете просто открыть STDERR, чтобы он был дублирован STDOUT в вашей программе Perl и избежать вызова оболочки для перенаправления. Это не работает:
open(STDERR, ">&STDOUT");
$alloutput = `cmd args`; # stderr still escapes Это не работает, потому что open() заставляет STDERR идти туда, куда STDOUT шёл в момент open(). Обратные кавычки затем заставляют STDOUT идти в строку, но не изменяют STDERR (который всё ещё идёт в старый STDOUT).
Обратите внимание, что вы должны использовать синтаксис перенаправления оболочки Bourne (sh(1)) в обратных кавычках, а не csh(1)! Подробности о том, почему открытия Perl's 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() содержит метасимволы оболочки, перл fork()ы, то exec() оболочку для декодирования метасимволов и, в конечном итоге, запуска желаемой программы. Если программа не может быть запущена, сообщение получает оболочка, а не Perl. Всё, что может узнать ваша программа Perl, — это удалось ли успешно запустить саму оболочку. Вы по-прежнему можете перехватить STDERR оболочки и проверить его на наличие сообщений об ошибках. См. "Как можно перехватить STDERR от внешней команды?" в этом документе или используйте модуль IPC::Open3.
Если в аргументе open() нет метасимволов оболочки, Perl запускает команду напрямую, без использования оболочки, и может корректно сообщить, запустилась ли команда.
Что неверно в использовании обратных кавычек в пустом контексте?
Строго говоря, ничего. С точки зрения стиля, это не лучший способ написания поддерживаемого кода. Perl имеет несколько операторов для запуска внешних команд. Обратные кавычки — один из них; они собирают вывод команды для использования в вашей программе. Функция system — другой вариант; она этого не делает.
Написание обратных кавычек в вашей программе отправляет ясное сообщение читателям вашего кода о том, что вы хотели собрать вывод команды. Зачем отправлять ясное сообщение, которое не соответствует действительности?
Рассмотрим эту строку:
`cat /etc/termcap`; Вы забыли проверить $? , чтобы убедиться, что программа вообще запустилась правильно. Даже если вы написали
print `cat /etc/termcap`; Этот код можно и нужно переписать как
system("cat /etc/termcap") == 0
or die "cat program failed!"; что будет выводить вывод команды cat по мере его генерации, вместо ожидания завершения программы для его вывода. Он также проверяет возвращаемое значение.
system также обеспечивает прямой контроль над тем, может ли происходить обработка подстановок оболочки, в то время как обратные кавычки этого не делают.
Как можно вызвать обратные кавычки без обработки оболочкой?
Это немного сложно. Вы не можете просто написать команду так:
@ok = `grep @opts '$search_string' @filenames`; Начиная с Perl 5.8.0, вы можете использовать open() с несколькими аргументами. Так же, как и списковые формы system() и exec(), никаких экранирований оболочки не происходит.
open( GREP, "-|", 'grep', @opts, $search_string, @filenames );
chomp(@ok = <GREP>);
close GREP; Вы также можете:
my @ok = ();
if (open(GREP, "-|")) {
while (<GREP>) {
chomp;
push(@ok, $_);
}
close GREP;
} else {
exec 'grep', @opts, $search_string, @filenames;
} Как и с system(), никаких экранирований оболочки не происходит, когда вы exec() список. Более подробные примеры можно найти в "Безопасные открытия конвейера" в perlipc.
Обратите внимание, что если вы используете Windows, ни одно решение этой проблемы даже не возможно. Несмотря на то, что Perl эмулирует fork(), вы всё равно окажетесь в затруднительном положении, так как Windows не имеет API в стиле argc/argv.
Почему моя программа не может считывать из STDIN после завершения ввода (^D в Unix, ^Z в MS-DOS)?
Это происходит только в том случае, если ваш Perl скомпилирован для использования stdio вместо perlio, что является значением по умолчанию. Некоторые (возможно, все) stdio устанавливают флаги ошибок и eof, которые вам может понадобиться очистить. Модуль POSIX определяет clearerr(), который вы можете использовать. Это технически правильный способ сделать это. Вот несколько менее надёжных решений:
-
Попробуйте сохранить указатель на позицию и перейти к ней, как в этом примере:
my $where = tell($log_fh); seek($log_fh, $where, 0); -
Если это не сработает, попробуйте перейти к другой части файла, а затем вернуться.
-
Если это не сработает, попробуйте перейти к другой части файла, прочитать что-то и вернуться назад.
-
Если это не сработает, откажитесь от вашего пакета 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
-
В строгом смысле этого сделать нельзя — скрипт выполняется как отдельный процесс от оболочки, из которой он был запущен. Изменения в процессе не отражаются в его родительском процессе — только в любых дочерних процессах, созданных после изменения. Существует магическая функция оболочки, которая может позволить вам это сымитировать, перенаправив вывод скрипта в вашу оболочку; обратите внимание на 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 Testers задают значение AUTOMATED_TESTING:
unless( $ENV{AUTOMATED_TESTING} ) {
print "Hello interactive tester!\n";
} Как сделать таймаут медленного события?
Используйте функцию alarm(), вероятно, в сочетании с обработчиком сигналов, как описано в "Сигналы" в perlipc и разделе о "Сигналах" в Camel. Вы можете вместо этого использовать более гибкий модуль Sys::AlarmCall, доступный из CPAN.
Функция alarm() не реализована во всех версиях Windows. Проверьте документацию для вашей конкретной версии Perl.
Как установить лимиты на CPU?
(внесён 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, и хотя некоторые люди могут знать все из них, вы, вероятно, не знаете. Однако все они должны быть в справке man perldiag. Если вы не найдете ошибку там, скорее всего, это не ошибка Perl.
Поиск каждого сообщения не является простым способом, поэтому позвольте Perl сделать это за вас. Используйте диагностический псевдоним, который превращает обычные сообщения Perl в более подробные обсуждения.
use diagnostics; Если вы не видите абзаца или двух расширенного обсуждения, это, вероятно, не сообщение Perl.
Как установить модуль с CPAN?
(предоставлено brian d foy)
Самый простой способ — использовать модуль CPAN, который выполняет это за вас, используя команду cpan, которая поставляется с Perl. Вы можете передать список модулей для установки:
$ 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 при создании файлов 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 $directory; Ключевым моментом в этой задаче является поиск каталога. Перед тем, как ваш скрипт сделает что-либо еще (например, chdir), вы можете получить текущий рабочий каталог с помощью модуля Cwd, который входит в комплект Perl:
BEGIN {
use Cwd;
our $directory = cwd;
}
use lib $directory; Вы можете сделать то же самое со значением $0, которое содержит имя скрипта. В нём может быть относительный путь, но rel2abs может преобразовать его в абсолютный путь. После того, как у вас есть
BEGIN {
use File::Spec::Functions qw(rel2abs);
use File::Basename qw(dirname);
my $path = rel2abs( $0 );
our $directory = dirname( $path );
}
use lib $directory; Модуль FindBin, который поставляется с Perl, может подойти. Он находит каталог текущего выполняемого скрипта и помещает его в $Bin, который можно использовать для построения правильного пути к библиотеке:
use FindBin qw($Bin); Вы также можете использовать local::lib для выполнения похожих задач. Установите модули, используя настройки local::lib, а затем используйте модуль в вашей программе:
use local::lib; # sets up a local lib at ~/perl5 См. документацию по local::lib для получения дополнительной информации.
Как добавить каталог в мой путь включения (@INC) во время выполнения?
Вот предложенные способы изменения пути включения, включая переменные среды, переключатели во время выполнения и операторы в коде:
-
переменная среды
PERLLIB -
$ export PERLLIB=/path/to/my/dir $ perl program.pl -
переменная среды
PERL5LIB -
$ export PERL5LIB=/path/to/my/dir $ perl program.pl -
переключатель командной строки
perl -Idir -
$ perl -I/path/to/my/dir program.pl -
псевдоним
lib -
use lib "$ENV{HOME}/myown_perllib"; - модуль local::lib
-
use local::lib; use local::lib "~/myown_perllib";
Где устанавливаются модули?
Модули устанавливаются на основе каждого конкретного случая (как указано в методах, описанных в предыдущем разделе) и в операционной системе. Все эти пути хранятся в @INC, которые вы можете отобразить с помощью однострочного оператора
perl -e 'print join("\n",@INC,"")' Та же информация отображается в конце вывода команды
perl -V Чтобы узнать, где находится исходный код модуля, используйте
perldoc -l Encode для отображения пути к модулю. В некоторых случаях (например, модуль AutoLoader) эта команда отобразит путь к отдельному файлу pod; сам модуль должен находиться в той же директории с расширением «pm».
Что такое socket.ph и где его получить?
Это файл в стиле Perl 4, определяющий значения системных сетевых констант. Иногда он создается с помощью h2ph при установке Perl, но иногда нет. Современные программы должны использовать use Socket; вместо него.
АВТОР И АВТОРСКИЕ ПРАВА
Авторские права (c) 1997-2010 Тома Кристиансена, Натана Торкинтона и других авторов, как указано. Все права защищены.
Эта документация бесплатна; вы можете распространять и/или изменять ее на тех же условиях, что и Perl.
Независимо от способа распространения, весь код в этом файле передан в общественное достояние. Вам разрешено и рекомендуется использовать этот код в своих программах для развлечения или с целью получения прибыли, как вам заблагорассудится. Простой комментарий, дающий зачет, был бы вежливым, но не является обязательным.
© 1993–2023 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.38.0/perlfaq8