perlfaq8
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ОПИСАНИЕ
- Как узнать, под какой операционной системой я работаю?
- Почему exec() не возвращает значение?
- Как выполнить сложные действия с клавиатурой/экраном/мышью?
- Как вывести текст в цвете?
- Как прочитать только один символ с клавиатуры без ожидания нажатия Enter?
- Как проверить, готов ли ввод с клавиатуры?
- Как очистить экран?
- Как получить размеры экрана?
- Как запросить у пользователя пароль?
- Как читать и записывать в последовательный порт?
- Как расшифровать зашифрованные файлы паролей?
- Как запустить процесс в фоновом режиме?
- Как перехватить управляющие символы/сигналы?
- Как изменить файл паролей shadow на Unix-системе?
- Как установить время и дату?
- Как сделать паузу sleep() или alarm() меньше секунды?
- Как измерить время меньше секунды?
- Как выполнить atexit() или setjmp()/longjmp()? (Обработка исключений)
- Почему моя программа сокетов не работает под System V (Solaris)? Что означает сообщение об ошибке "Protocol not supported"?
- Как вызвать уникальные 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
ОПИСАНИЕ
Этот раздел FAQ Perl посвящен вопросам взаимодействия с операционной системой. Тематика включает межпроцессное взаимодействие (IPC), управление пользовательским интерфейсом (клавиатура, экран и указательные устройства) и все остальное, что не связано с манипуляциями данными.
Прочитайте часто задаваемые вопросы и документацию, специфичные для вашей операционной системы (например, perlvms, perlplan9, ...). Они должны содержать более подробную информацию о тонкостях вашего Perl.
Как узнать, под какой операционной системой я работаю?
Переменная $^O ($OSNAME если вы используете English ) содержит указание имени операционной системы (не её номер версии), для которой был скомпилирован ваш Perl.
Почему exec() не возвращает значение?
(внесено brian d foy)
Задача функции exec — преобразовать ваш процесс в другую команду и никогда не возвращаться. Если этого вы не хотите, не используйте exec. :)
Если вы хотите запустить внешнюю команду, но сохранить свой процесс Perl, посмотрите на использование канала open, fork, или system.
Как выполнить сложные действия с клавиатурой/экраном/мышью?
Способы доступа и управления клавиатурой, экраном и указательными устройствами ("мышами") зависят от системы. Попробуйте следующие модули:
- Клавиатура
-
Term::Cap Standard perl distribution Term::ReadKey CPAN Term::ReadLine::Gnu CPAN Term::ReadLine::Perl CPAN Term::Screen CPAN - Экран
-
Term::Cap Standard perl distribution Curses CPAN Term::ANSIColor CPAN - Мышь
-
Tk CPAN Wx CPAN Gtk2 CPAN Qt4 kdebindings4 package
Некоторые из этих конкретных случаев показаны как примеры в других ответах в этом разделе perlfaq.
Как вывести текст в цвете?
В общем случае этого нельзя сделать, потому что вы не знаете, есть ли у получателя цветной дисплей. Если вы знаете, что у получателя терминал ANSI, понимающий цвет, вы можете использовать модуль Term::ANSIColor из CPAN:
use Term::ANSIColor;
print color("red"), "Stop!\n", color("reset");
print color("green"), "Go!\n", color("reset"); Или так:
use Term::ANSIColor qw(:constants);
print RED, "Stop!\n", RESET;
print GREEN, "Go!\n", RESET; Как прочитать только один символ с клавиатуры без ожидания нажатия Enter?
Управление буферизацией ввода — очень зависимая от системы задача. На многих системах вы можете просто использовать команду stty, как показано в "getc" в perlfunc, но, как видите, это уже приводит к проблемам с переносимостью.
open(TTY, "+</dev/tty") or die "no tty: $!";
system "stty cbreak </dev/tty >/dev/tty 2>&1";
$key = getc(TTY); # perhaps this works
# OR ELSE
sysread(TTY, $key, 1); # probably this does
system "stty -cbreak </dev/tty >/dev/tty 2>&1"; Модуль Term::ReadKey из CPAN предлагает простой в использовании интерфейс, который должен быть эффективнее, чем вызов stty для каждого символа. Он даже включает ограниченную поддержку Windows.
use Term::ReadKey;
ReadMode('cbreak');
$key = ReadKey(0);
ReadMode('normal'); Однако, для использования кода необходим работающий компилятор C и возможность использовать его для сборки и установки модуля CPAN. Вот решение с использованием стандартного модуля POSIX, который уже есть на вашей системе (если ваша система поддерживает POSIX).
use HotKey;
$key = readkey(); И вот модуль HotKey, который скрывает несколько загадочные вызовы для манипулирования структурами POSIX termios.
# HotKey.pm
package HotKey;
use strict;
use warnings;
use parent 'Exporter';
our @EXPORT = qw(cbreak cooked readkey);
use POSIX qw(:termios_h);
my ($term, $oterm, $echo, $noecho, $fd_stdin);
$fd_stdin = fileno(STDIN);
$term = POSIX::Termios->new();
$term->getattr($fd_stdin);
$oterm = $term->getlflag();
$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;
sub cbreak {
$term->setlflag($noecho); # ok, so i don't want echo either
$term->setcc(VTIME, 1);
$term->setattr($fd_stdin, TCSANOW);
}
sub cooked {
$term->setlflag($oterm);
$term->setcc(VTIME, 0);
$term->setattr($fd_stdin, TCSANOW);
}
sub readkey {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}
END { cooked() }
1; Как проверить, готов ли ввод с клавиатуры?
Самый простой способ сделать это — прочитать символ в режиме без блокировки с помощью модуля Term::ReadKey из CPAN, передав ему аргумент -1, чтобы указать, что не нужно блокировать:
use Term::ReadKey;
ReadMode('cbreak');
if (defined (my $char = ReadKey(-1)) ) {
# input was waiting and it was $char
} else {
# no input was waiting
}
ReadMode('normal'); # restore normal tty settings Как очистить экран?
(представлено brian d foy)
Чтобы очистить экран, нужно просто напечатать специальную последовательность, которая сообщает терминалу очистить экран. После получения этой последовательности выведите ее, когда захотите очистить экран.
Вы можете использовать модуль Term::ANSIScreen, чтобы получить специальную последовательность. Импортируйте функцию cls (или тег :screen):
use Term::ANSIScreen qw(cls);
my $clear_screen = cls();
print $clear_screen; Модуль Term::Cap также может получить специальную последовательность, если вы хотите работать с низкоуровневыми деталями управления терминалом. Метод Tputs возвращает строку для заданного параметра:
use Term::Cap;
my $terminal = Term::Cap->Tgetent( { OSPEED => 9600 } );
my $clear_screen = $terminal->Tputs('cl');
print $clear_screen; В Windows можно использовать модуль Win32::Console. После создания объекта для файлового дескриптора вывода, который вы хотите повлиять, вызовите метод Cls:
Win32::Console;
my $OUT = Win32::Console->new(STD_OUTPUT_HANDLE);
my $clear_string = $OUT->Cls;
print $clear_screen; Если у вас есть программа командной строки, которая выполняет задачу, вы можете вызвать ее в обратных кавычках, чтобы получить то, что она выведет, и использовать это позже:
my $clear_string = `clear`;
print $clear_string; Как получить размер экрана?
Если у вас установлен модуль Term::ReadKey из CPAN, вы можете использовать его для получения ширины и высоты в символах и пикселях:
use Term::ReadKey;
my ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize(); Это более портативно, чем сырые ioctl, но не так наглядно:
require './sys/ioctl.ph';
die "no TIOCGWINSZ " unless defined &TIOCGWINSZ;
open(my $tty_fh, "+</dev/tty") or die "No tty: $!";
unless (ioctl($tty_fh, &TIOCGWINSZ, $winsize='')) {
die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ;
}
my ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
print "(row,col) = ($row,$col)";
print " (xpixel,ypixel) = ($xpixel,$ypixel)" if $xpixel || $ypixel;
print "\n"; Как запросить у пользователя пароль?
(Этот вопрос не имеет отношения к сети. Обратитесь к другому разделу FAQ для этого.)
Пример этого есть в "crypt" в perlfunc. Сначала установите режим терминала "без эха", а затем просто считайте пароль в обычном режиме. Вы можете сделать это с помощью функции старой модели ioctl(), управления терминалом POSIX (см. POSIX или его документацию в Camel Book) или вызова программы stty с различной степенью портативности.
Вы также можете сделать это для большинства систем, используя модуль Term::ReadKey из CPAN, который легче в использовании и теоретически более портативен.
use Term::ReadKey;
ReadMode('noecho');
my $password = ReadLine(0); Как читать и писать в последовательный порт?
Это зависит от операционной системы, на которой работает ваша программа. В случае Unix последовательные порты будут доступны через файлы в /dev; в других системах имена устройств, несомненно, будут отличаться. Несколько проблемных областей, общих для всех взаимодействий с устройствами:
- запирающие файлы
-
Ваша система может использовать запирающие файлы для управления множественным доступом. Убедитесь, что вы следуете правильной процедуре. Непредсказуемое поведение может возникнуть при одновременном чтении из одного устройства несколькими процессами.
- режим открытия
-
Если вы ожидаете использовать как операции чтения, так и записи в устройство, вам нужно открыть его для обновления (см. "open" в perlfunc для получения подробностей). Вы можете открыть его без риска блокировки, используя
sysopen()иO_RDWR|O_NDELAY|O_NOCTTYиз модуля Fcntl (часть стандартного дистрибутива Perl). См. "sysopen" в perlfunc для получения дополнительной информации об этом подходе. - конец строки
-
Некоторые устройства будут ожидать "\r" в конце каждой строки вместо "\n". В некоторых портах Perl "\r" и "\n" отличаются от их обычных (Unix) ASCII значений "\015" и "\012". Вам может потребоваться ввести нужные числовые значения непосредственно, используя восьмеричный ("\015"), шестнадцатеричный ("0x0D") или как спецификацию управляющего символа ("\cM").
print DEV "atv1\012"; # wrong, for some devices print DEV "atv1\015"; # right, for some devicesХотя с обычными текстовыми файлами "\n" сработает, все же нет единой схемы для завершения строки, которая была бы портативной между Unix, DOS/Win и Macintosh, кроме как завершать все концы строк "\015\012" и удалять то, что вам не нужно, из вывода. Это особенно касается ввода-вывода сокетов и автоматической записи, которые обсуждаются далее.
- очистка вывода
-
Если вы ожидаете, что символы попадут на ваше устройство, когда вы
print()их, вы захотите использовать автоматическую очистку этого файлового дескриптора. Вы можете использоватьselect()и переменную$|для управления автоматической очисткой (см. "$|" в perlvar и "select" в perlfunc, или perlfaq5, "Как очистить/разобмесить файловый дескриптор вывода? Почему я должен это делать?"):my $old_handle = select($dev_fh); $| = 1; select($old_handle);Вы также увидите код, который делает это без временной переменной, как в
select((select($deb_handle), $| = 1)[0]);Или если вам не жалко ввести несколько тысяч строк кода просто потому, что вы боитесь маленькой переменной
$|:use IO::Handle; $dev_fh->autoflush(1);Как упоминалось в предыдущем пункте, это по-прежнему не работает при использовании ввода-вывода сокетов между Unix и Macintosh. В этом случае вам придется жестко закодировать ваши символы завершения строки.
- ввод без блокировки
-
Если вы выполняете блокирующее
read()илиsysread(), вам нужно организовать обработчик таймера с помощью команды `alarm` (см. "alarm" в perlfunc). Если у вас открытие без блокировки, вы, вероятно, будете иметь чтение без блокировки, а это означает, что вам может понадобиться использоватьselect()с 4 аргументами, чтобы определить, готов ли ввод-вывод на этом устройстве (см. "select" в perlfunc.
Пытаясь прочитать данные с устройства идентификации вызывающего абонента, злосчастный Jamie Zawinski <jwz@netscape.com>, после многих стонов и борьбы с sysread, sysopen, функциями POSIX tcgetattr и различными другими функциями, которые работают ночью, наконец, придумал это:
sub open_modem {
use IPC::Open2;
my $stty = `/bin/stty -g`;
open2( \*MODEM_IN, \*MODEM_OUT, "cu -l$modem_device -s2400 2>&1");
# starting cu hoses /dev/tty's stty settings, even when it has
# been opened on a pipe...
system("/bin/stty $stty");
$_ = <MODEM_IN>;
chomp;
if ( !m/^Connected/ ) {
print STDERR "$0: cu printed `$_' instead of `Connected'\n";
}
} Как расшифровать зашифрованные файлы паролей?
Вы тратите много денег на специализированное оборудование, но это должно вас обсуждать.
Серьезно, вы не можете, если это файлы паролей Unix — система паролей Unix использует одностороннее шифрование. Это больше похоже на хеширование, чем на шифрование. Лучшее, что вы можете сделать, — проверить, не хеширует ли что-то другое до той же строки. Вы не можете преобразовать хеш обратно в исходную строку. Такие программы, как Crack, могут принудительно (и разумно) пытаться угадать пароли, но не гарантируют быстрой удачи.
Если вы беспокоитесь о том, что пользователи выбирают плохие пароли, вы должны активно проверять, когда они пытаются изменить свой пароль (например, модифицируя passwd(1)).
Как запустить процесс на заднем плане?
(представлено brian d foy)
Нет единого способа запускать код на заднем плане, поэтому вам не нужно ждать его завершения, прежде чем ваша программа перейдет к другим задачам. Управление процессами зависит от вашей конкретной операционной системы, и многие методы описаны в perlipc.
Несколько модулей CPAN могут помочь, включая IPC::Open2 или IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background и Win32::Process. Есть много других модулей, которые вы можете использовать, поэтому проверьте эти пространства имен, чтобы найти другие варианты.
Если вы находитесь в системе типа Unix, вы можете обойтись вызовом системы, где вы ставите & в конце команды:
system("cmd &") Вы также можете попробовать использовать fork, как описано в perlfunc (хотя это то же самое, что делают многие модули за вас).
-
И основной процесс, и запущенный в фоновом режиме процесс ("дочерний" процесс) используют одни и те же файловые дескрипторы STDIN, STDOUT и STDERR. Если оба пытаются получить к ним доступ одновременно, могут произойти странные вещи. Вы можете закрыть или снова открыть их для дочернего процесса. Вы можете обойти это с помощью
openтрубы (см. "open" в perlfunc), но на некоторых системах это означает, что дочерний процесс не может пережить родительский. - Сигналы
-
Вам нужно перехватить сигнал SIGCHLD, а возможно, и SIGPIPE. SIGCHLD отправляется, когда фоновый процесс завершается. SIGPIPE отправляется, когда вы записываете в файловый дескриптор, дочерний процесс которого закрыл (неперехваченный SIGPIPE может привести к тому, что ваша программа молча прекратит работу). Это не проблема с
system("cmd&"). - Зомби
-
Вы должны быть готовы "собирать" дочерний процесс, когда он завершается.
$SIG{CHLD} = sub { wait }; $SIG{CHLD} = 'IGNORE';Вы также можете использовать двойное разветвление. Сразу же
wait()создаёте своего первого ребёнка, а демон инициализацииwait()создаст вашего внука, как только он завершит работу.unless ($pid = fork) { unless (fork) { exec "what you really wanna do"; die "exec failed!"; } exit 0; } waitpid($pid, 0);См. "Сигналы" в perlipc для получения других примеров кода, чтобы сделать это. Зомби не являются проблемой с
system("prog &").
Как перехватить управляющие символы/сигналы?
Вы фактически не "перехватываете" управляющий символ. Вместо этого этот символ генерирует сигнал, который отправляется в текущую группу процессов терминала на переднем плане, которую вы затем перехватываете в своем процессе. Сигналы документированы в "Сигналы" в perlipc и в разделе "Сигналы" в Camel.
Вы можете установить значения хеша %SIG, чтобы они были функциями, которые вы хотите использовать для обработки сигнала. После того, как perl перехватывает сигнал, он ищет в %SIG ключ с таким же именем, как и сигнал, затем вызывает подпрограмму со значением этого ключа.
# as an anonymous subroutine
$SIG{INT} = sub { syswrite(STDERR, "ouch\n", 5 ) };
# or a reference to a function
$SIG{INT} = \&ouch;
# or the name of the function as a string
$SIG{INT} = "ouch"; В версиях Perl до 5.8 в исходном коде на C были обработчики сигналов, которые перехватывали сигнал и, возможно, запускали функцию Perl, которую вы установили в %SIG. Это нарушало правила обработки сигналов на этом уровне, что приводило к сбою perl. С версии 5.8.0 perl проверяет значение %SIG **после** того, как сигнал был перехвачен, а не во время перехвата. Предыдущие версии этого ответа были неверными.
Как изменить файл паролей shadow в Unix-системе?
Если perl был установлен правильно, а ваша библиотека 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 каждого пакета вызывается при завершении программы или потока. Подробнее о блоках END см. в руководстве perlmod.
Например, вы можете использовать это, чтобы убедиться, что ваша программа-фильтр завершила вывод, не заполнив диск:
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. Правильный способ справиться с этим — «использовать Socket», чтобы получить правильные значения.
Обратите внимание, что, хотя SunOS и Solaris совместимы на бинарном уровне, эти значения отличаются. Попробуйте понять.
Как вызвать уникальные функции C моей системы из Perl?
В большинстве случаев для этого нужно написать внешний модуль — см. ответ на вопрос "Где можно узнать о связывании C с Perl? [h2xs, xsubpp]". Однако, если функция является системным вызовом, а ваша система поддерживает syscall(), вы можете использовать функцию syscall (описанную в perlfunc).
Не забудьте проверить модули, которые поставлялись с вашей дистрибуцией, и CPAN — возможно, кто-то уже написал модуль для этого. В Windows попробуйте Win32::API. В macOS попробуйте Mac::Carbon. Если ни один модуль не имеет интерфейса к функции C, вы можете вставить немного C в свой Perl-код с помощью Inline::C.
Где получить файлы заголовков для ioctl() или syscall()?
Исторически эти файлы генерировались инструментом h2ph, входящим в стандартную дистрибуцию Perl. Эта программа преобразует директивы cpp(1) в файлах заголовков C в файлы, содержащие определения подпрограмм, например, SYS_getitimer(), которые вы можете использовать в качестве аргументов для своих функций. Она не работает идеально, но обычно выполняет большую часть работы. Простые файлы, такие как errno.h, syscall.h и socket.h, были хороши, но такие сложные, как ioctl.h, почти всегда требуют ручного редактирования. Вот как установить файлы *.ph:
1. Become the super-user
2. cd /usr/include
3. h2ph *.h */*.h Если ваша система поддерживает динамическую загрузку, по соображениям портативности и здравомыслия вы, вероятно, должны использовать h2xs (также входит в стандартную дистрибуцию Perl). Этот инструмент преобразует файлы заголовков C в расширения Perl. См. perlxstut, чтобы узнать, как начать работу с h2xs.
Если ваша система не поддерживает динамическую загрузку, вы всё равно, вероятно, должны использовать h2xs. См. perlxstut и ExtUtils::MakeMaker для получения дополнительной информации (коротко: просто используйте make perl вместо обычного make, чтобы перестроить Perl с новым статическим расширением).
Почему скрипты setuid Perl жалуются на проблемы с ядром?
В некоторых операционных системах существуют ошибки в ядре, которые делают скрипты setuid небезопасными по своей природе. Perl предлагает ряд решений (описанных в perlsec), чтобы обойти такие системы.
Как открыть канал связи как для ввода, так и для вывода команды?
Модуль IPC::Open2 (входит в стандартную дистрибуцию Perl) — это простой в использовании подход, который внутри использует pipe(), fork(), и exec() для выполнения задачи. Однако обязательно ознакомьтесь с предупреждениями о тупиках в его документации (см. IPC::Open2). См. "Взаимодействие с другим процессом" в perlipc и "Взаимодействие с самим собой" в perlipc.
Вы также можете использовать модуль IPC::Open3 (входит в стандартную дистрибуцию Perl), но имейте в виду, что у него другой порядок аргументов, чем у IPC::Open2 (см. IPC::Open3).
Почему я не могу получить вывод команды с помощью system()?
Вы путаете назначение system() и обратных кавычек (``). system() выполняет команду и возвращает информацию об успешном завершении (как значение 16 бит: низкие 7 бит — это сигнал, по которому процесс завершился, если таковой был, а высокие 8 бит — фактическое возвращаемое значение). Обратные кавычки (``) выполняют команду и возвращают то, что она отправила в STDOUT.
my $exit_status = system("mail-users");
my $output_string = `ls`; Как захватить STDERR от внешней команды?
Существует три основных способа выполнения внешних команд:
system $cmd; # using system()
my $output = `$cmd`; # using backticks (``)
open (my $pipe_fh, "$cmd |"); # using open() С помощью system(), как STDOUT, так и STDERR попадут в то же место, что и STDOUT и STDERR скрипта, если команда system() не перенаправит их. Обратные кавычки и open() читают только STDOUT вашей команды.
Вы также можете использовать функцию open3() из IPC::Open3. Бенджамин Голдберг предоставляет примеры кода:
Чтобы захватить 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.
Чтобы захватить STDOUT и STDERR команды вместе:
$output = `cmd 2>&1`; # either with backticks
$pid = open(PH, "cmd 2>&1 |"); # or with an open pipe
while (<PH>) { } # plus a read Чтобы захватить STDOUT команды, но игнорировать STDERR:
$output = `cmd 2>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read Чтобы захватить STDERR команды, но игнорировать STDOUT:
$output = `cmd 2>&1 1>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>&1 1>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read Чтобы поменять STDOUT и STDERR команды, чтобы захватить STDERR, но оставить STDOUT на нашем старом STDERR:
$output = `cmd 3>&1 1>&2 2>&3 3>&-`; # either with backticks
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# or with an open pipe
while (<PH>) { } # plus a read Чтобы прочитать STDOUT и STDERR команды по отдельности, легче всего перенаправить их в отдельные файлы, а затем прочитать из этих файлов, когда программа завершит работу:
system("program args 1>program.stdout 2>program.stderr"); Порядок важен во всех этих примерах. Это потому, что оболочка обрабатывает перенаправления дескрипторов файлов строго слева направо.
system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile"); Первая команда отправляет как стандартный вывод, так и стандартную ошибку во временный файл. Вторая команда отправляет туда только старый стандартный вывод, а старая стандартная ошибка отображается на старом стандартном выводе.
Почему open() не возвращает ошибку при неудачном открытии канала?
Если второй аргумент в команде с использованием | open() содержит метасимволы оболочки, символы перла fork() , то exec() оболочка для декодирования метасимволов и, в конечном итоге, запуска желаемой программы. Если программа не может быть запущена, сообщение получает оболочка, а не Perl. Ваш скрипт Perl может только узнать, удалось ли запустить оболочку. Вы всё ещё можете получить STDERR оболочки и проверить его на наличие сообщений об ошибках. См. "Как можно получить STDERR от внешней команды?" в этом документе или используйте модуль IPC::Open3.
Если в аргументе команды open() нет метасимволов оболочки, Perl запускает команду напрямую, без использования оболочки, и может корректно сообщить, стартовала ли команда.
Что не так с использованием backticks в контексте void?
Строго говоря, ничего. С точки зрения стиля, это не лучший способ написания поддерживаемого кода. Perl имеет несколько операторов для запуска внешних команд. Backticks — один из них; они собирают вывод команды для использования в вашей программе. Функция system — другой вариант; она этого не делает.
Написание backticks в вашей программе ясно сообщает читателям кода, что вы хотели собрать вывод команды. Зачем посылать ясное сообщение, которое не соответствует действительности?
Рассмотрим эту строку:
`cat /etc/termcap`; Вы забыли проверить $? , чтобы убедиться, что программа вообще запустилась корректно. Даже если вы написали
print `cat /etc/termcap`; этот код можно и, вероятно, нужно переписать как
system("cat /etc/termcap") == 0
or die "cat program failed!"; что выведет вывод команды cat по мере его генерации, вместо ожидания завершения программы для вывода. Он также проверяет возвращаемое значение.
system также обеспечивает прямой контроль над тем, может ли произойти обработка шаблонов оболочки, в то время как backticks не могут.
Как можно вызвать backticks без обработки оболочкой?
Это немного сложно. Вы не можете просто написать команду так:
@ok = `grep @opts '$search_string' @filenames`; Начиная с Perl 5.8.0, вы можете использовать open() с несколькими аргументами. Так же, как и в списковых формах system() и exec(), избегается обработка оболочкой.
open( GREP, "-|", 'grep', @opts, $search_string, @filenames );
chomp(@ok = <GREP>);
close GREP; Также можно:
my @ok = ();
if (open(GREP, "-|")) {
while (<GREP>) {
chomp;
push(@ok, $_);
}
close GREP;
} else {
exec 'grep', @opts, $search_string, @filenames;
} Как и с system(), нет обработки оболочки, когда вы exec() список. Дополнительные примеры можно найти в "Safe Pipe Opens" в perlipc.
Обратите внимание, что если вы используете Windows, то никакого решения этой проблемы не существует. Даже если Perl эмулирует fork(), вы всё равно застрянете, потому что Windows не имеет API в стиле argc/argv.
Почему мой скрипт не может читать из STDIN после того, как я дал ему EOF (^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
-
В строгом смысле, это невозможно — скрипт выполняется как отдельный процесс от оболочки, из которой он был запущен. Изменения в процессе не отображаются в его родителе — только в любом дочернем процессе, созданном после изменения. Существуют магические возможности оболочки, которые могут позволить вам подделать это, перенаправив выход скрипта в вашей оболочке; ознакомьтесь со списком часто задаваемых вопросов 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(), вероятно, в сочетании с обработчиком сигналов, как описано в "Signals" в 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 секунд непрерывного использования CPU (а не "стенных" часов), процессу будет отправлен сигнал (XCPU на некоторых системах), который, если не будет перехвачен, приведёт к завершению процесса. Если сигнал перехватывается, то после ещё 10 секунд (в общей сложности 20 секунд) процесс будет убит с помощью неперехватываемого сигнала.
См. BSD::Resource и документацию вашей системы для подробностей.
Как избежать зомби на Unix-системе?
Используйте код уборщика из "Signals" в perlipc для вызова wait() при получении сигнала SIGCHLD или используйте технику двойного разветвления, описанную в "How do I start a process in the background?" в 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/.
Как заставить систему() завершить работу при нажатии Control-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 сделать это за вас. Используйте прагму diagnostics, чтобы превратить обычные сообщения Perl в более подробные обсуждения темы.
use diagnostics; Если вы не получите абзаца или двух расширенного обсуждения, это, возможно, не сообщение Perl.
Как установить модуль с CPAN?
(внесено brian d foy)
Самый простой способ — использовать команду cpan, которая поставляется с Perl, и пусть модуль CPAN сделает это за вас. Вы можете указать список модулей для установки:
$ cpan IO::Interactive Getopt::Whatever Если вы предпочитаете CPANPLUS, это так же просто:
$ cpanp i IO::Interactive Getopt::Whatever Если вы хотите установить дистрибутив из текущей директории, вы можете сказать CPAN.pm установить . (полную точку):
$ cpan . См. документацию по любой из этих команд, чтобы узнать, что ещё можно сделать.
Если вы хотите попробовать установить дистрибутив самостоятельно, разрешив все зависимости самостоятельно, следуйте одному из двух возможных путей сборки.
Для дистрибутивов, использующих Makefile.PL:
$ perl Makefile.PL
$ make test install Для дистрибутивов, использующих Build.PL:
$ perl Build.PL
$ ./Build test
$ ./Build install Некоторые дистрибутивы могут потребовать ссылки на библиотеки или другой сторонний код, и их последовательности сборки и установки могут быть более сложными. Проверьте любые файлы README или INSTALL, которые вы можете найти.
В чём разница между require и use?
(внесено brian d foy)
Perl выполняет оператор require во время выполнения. После того, как Perl загрузит, скомпилирует и выполнит файл, он больше ничего не делает. Оператор use эквивалентен выполнению require во время компиляции, но Perl также вызывает метод import для загруженного пакета. Эти два оператора эквивалентны:
use MODULE qw(import list);
BEGIN {
require MODULE;
MODULE->import(import list);
} Однако вы можете подавить вызов import с помощью явного, пустого списка импорта. Оба этих оператора по-прежнему выполняются во время компиляции:
use MODULE ();
BEGIN {
require MODULE;
} Так как use также вызовет метод import, фактическое значение для MODULE должно быть словом без каких-либо символов. То есть, use не может загружать файлы по имени, хотя require может:
require "$ENV{HOME}/lib/Foo.pm"; # no @INC searching! См. запись для use в perlfunc для получения дополнительной информации.
Как сохранить свой собственный каталог модулей/библиотек?
При создании модулей сообщите Perl, где установить модули.
Если вы хотите установить модули для своего собственного использования, самым простым способом может быть local::lib, который вы можете загрузить с CPAN. Он устанавливает различные параметры установки для вас и использует те же параметры в ваших программах.
Если вы хотите больше гибкости, вам нужно настроить ваш клиент CPAN для вашей конкретной ситуации.
Для дистрибутивов на основе Makefile.PL, используйте параметр INSTALL_BASE при генерации файлов 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 Tom Christiansen, Nathan Torkington и других авторов, как указано. Все права защищены.
Эта документация распространяется свободно; вы можете перераспределять её и/или изменять её на тех же условиях, что и Perl сам по себе.
Независимо от его распространения, все примеры кода в этом файле объявляются общественным достоянием. Вам разрешается и рекомендуется использовать этот код в своих программах для развлечения или коммерческих целей, как вам заблагорассудится. Простая ссылка в коде с указанием авторства была бы вежливой, но не обязательной.
© 1993–2021 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.36.0/perlfaq8