.
Параметры командной строки
В Perl предусмотрены специальные параметры командной строки - -n, -р и -i,
упрощающие написание фильтров и однострочных программ.
Параметр -п помещает исходный текст программы внутрь цикла while (<>). Обычно он используется в фильтрах типа дгер или программах, которые накапливают статистику по прочитанным данным.
Пример 7.1. ffndlogini
#!/usr/bin/perl
# findlogini - вывести все строки, содержащие подстроку "login"
while (<>) { # Перебор файлов в командной строке print if /login/;
}
Программу из примера 7.1 можно записать так, как показано в примере 7.2.
Пример 7.2. rindlogin2
#!/usr/bin/perl -n
# findlogin2 - вывести все строки, содержащие подстроку "login"
print if /login/;
Параметр -n может объединяться с -е для выполнения кода Perl из командной строки:
% perl -ne 'print if /login/'
Параметр -р аналогичен -n, однако он добавляет print в конец цикла. Обычно он используется в программах для преобразования входных данных.
Пример 7.3. lowercasel
#!/usr/bin/perl
# lowercase - преобразование всех строк в нижний регистр
use locale;
while (<>) { # Перебор в командной строке
s/(["\WO-9_])/\l$1/g; # Перевод всех букв в нижний регистр
print;
}
Программу из примера 7.3 можно записать так, как показано в примере 7.4.
Пример 7.4. lowercase2
#!/usr/bin/perl -р
# lowercase - преобразование всех строк в нижний регистр
use locale;
s/(["\WO-9_])/\l$1/g; # Перевод всех букв в нижний регистр
Или непосредственно в командной строке следующего вида:
% perl -Miocale -pe 's/(["\WO-9_])/\1$1/g'
При использовании -п или -р для неявного перебора входных данных для всего цикла негласно создается специальная метка LINE:. Это означает, что из внутреннего цикла можно перейти к следующей входной записи командой next LINE (аналог next в awk). При закрытии ARGV происходит переход к следующему файлу (аналог next file в awk). Обе возможности продемонстрированы в примере 7.5.
Пример 7.5. countchunks
#!/usr/bin/perl -n
# countchunks - подсчет использованных слов
# с пропуском комментариев. При обнаружении __END__ или __DATA__
# происходит переход к следующему файлу.
for (split /\W+/) {
next LINE if /"#/;
close ARGV if /__(DATA|END)__/;
$chunks++:
} ED { print "Found $chunks chunks\n" }
В файле .history, создаваемым командным интерпретатором tcsh, перед каждой строкой указывается время, измеряемое в секундах с начала эпохи:
#+0894382237 less /etc/motd "+0894382239 vi '/.exrc
#+0894382242 date
#+0894382239 who
#+0894382288 telnet home
Простейшая однострочная программа приводит его к удобному формату:
%perl -pe 's/"#\+(\d+)\n/localtime($1) . " "/е'
Tue May 5 09:30:37 1998 less /etc/motd
Tue May 5 09:30:39 1998 vi "/.exi-c
Tue May 5 09:30:42 1998 date
Tue May 5 09:30:42 1998 who
Tue May 5 09:30:28 1998 telnet home
Параметр -i изменяет каждый файл в командной строке. Он описан в рецепте 7.9 и обычно применяется в сочетании с -р.
Для работы с национальными наборами символов используется директива use
locale.
Смотри также: perlmn(1); рецепты 7.9; 16.6.
Проблема
Требуется обновить содержимое файла на месте. При этом допускается применение временного файла.
Решение
Прочитайте данные из исходного файла, запишите изменения во временный файл и затем переименуйте временный файл в исходный:
open(OLD, "< $old") or die "can't open $old: $!";
open(NEW, "< $new") or die "can't open $new: $!";
select(NEW); N Новый файловый манипулятор,
# используемый print по умолчанию
while () {
# Изменить $_, затем...
print NEW $_ or die "can't write $new: $!";
}
close(OLD) or die "can't close $old: $!";
close(NEW) or die "can't close $new: $!";
rename($old, "$old,orig") or die "can't rename $old to $old.orig: $!";
rename($new, $old) or die "can't rename $new to Sold: $!";
Такой способ лучше всего приходит для обновления файлов "на месте".
Комментарий
Этот метод требует меньше памяти, чем другие подходы, не использующие временных файлов. Есть и другие преимущества - наличие резервной копии файла, надежность и простота программирования.
Показанная методика позволяет внести в файл те же изменения, что и другие версии, не использующие временных файлов. Например, можно вставить новые строки перед 20-й строкой файла:
while () {
if ($. == 20) {
print NEW "Extra line 1\n";
print NEW "Extra line 2\n":
} print NEW $_;
}
Или удалить строки с 20 по 30:
while () {
next if 20 .. 30;
print NEW $_;
}
Обратите внимание: функция rename работает лишь в пределах одного каталога, поэтому временный файл должен находиться в одном каталоге с модифицируемым.
Программист-перестраховщик непременно заблокирует файл на время обновления.
Смотри также: Рецепты 7.1; 7.9-7.10
Проблема
Требуется обновить файл на месте из командной строки, но вам лень' возиться с файловыми операциями из рецепта 7.8.
Решение
Воспользуйтесь параметрами -i и -р командной строки Perl. Запишите свою программу в виде строки:
% perl -i.orig -p 'ФИЛЬТР' файл"! файл2 файлЗ ... Или воспользуйтесь параметрами в самой программе:
#!/usr/bin/perl -i.orig -p
# Фильтры
Комментарий
Параметр командной строки -i осуществляет непосредственную модификацию файлов. Он создает временный файл, как и в предыдущем рецепте, однако Perl берет на себя все утомительные хлопоты с слайдами. Используйте -i в сочетании с -р (см. рецепт 7.7), чтобы превратить:
% perl -pi.orig -e 's/DATE/localtime/e' в следующий фрагмент:
while (<>) {
if ($ARGV ne $oldargv) { # Мы перешли к следующему файлу?
rename($ARGV, $ARGV . '.orig');
open(ARGVOUT, ">$ARGV"); # Плюс проверка ошибок
select(ARGVOUT);
$oldargv = $argv;
} s/DATE/localtime/e;
} continue{
Конечно, имеется в виду лень творческая, а не греховная.
print;
} select (STDOUT); # Восстановить стандартный вывод
Параметр -i заботится о создании резервных копий (если вы не желаете сохранять исходное содержимое файлов, используйте -i вместо -i.orig), а -р заставляет Perl перебирать содержимое файлов, указанных в командной строке (или STDIN при их отсутствии).
Приведенная выше однострочная программа приводит данные:
Dear Sir/Madam/Ravenous Beast,
As of DATE, our records show your account is overdue. Please settle by the end of the month. Yours in cheerful usury,
--A. Moneylender
к следующему виду:
Dear Sir/Madam/Ravenous Beast,
As of Sat Apr 25 12:28:33 1998, our records show your account is overdue. Please settle by the end of the month. Yours in cheerful usury,
--A. Moneylender
Этот параметр заметно упрощает разработку и чтение программ-трансляторов. Например, следующий фрагмент заменяет все изолированные экземпляры "hisvar" на "hervar" во всех файлах С, C++ и у асе:
%perl -i.old -pe 's{\bhisvar\b}{hervar}g' *.[Cchy]
%рег1 -i.old -ne 'print unless /"STARTS/ .. /"END$/' bigfile.text
Действие -i может включаться и выключаться с помощью специальной переменной $"1. Инициализируйте @ARGV и затем примените о так, как применили бы -i для командной строки:
# Организовать перебор файлов *.с в текущем каталоге,
# редактирование на месте и сохранение старого файла с расширением .orig
local $"I = '.orig'; # Эмулировать -i.orig
local @>ARGV = glob("*.c"); # Инициализировать список файлов
while (<>) {
if ($. - 1) {
print "This line should appear at the top of each file\n";
}
s/\b(p)earl\b/{1}erl/ig; # Исправить опечатки с сохранением регистра
print;
} continue {close ARGV if eof}
Учтите, что при создании резервной копии предыдущая резервная копня унпч тожается.
Смотри также: Описание переменных $"1 и $. в perlvar(1) описание оператора . . в разделе "Range Operator" perlop(1); perlmn(1).
Проблема
Требуется вставить, удалить или изменить одну или несколько строк файла. При этом вы не хотите (или не можете) создавать временный файл.
Решение
Откройте файл в режиме обновления ("+<"), прочитайте все его содержимое в массив строк, внесите необходимые изменения в массиве, после чего перезапишите файл и выполните усечение до текущей позиции.
open(FH, "+< FILE" or die "Opening: $!";
@ARRAY = ;
# Модификация массива ARRAY
seek(FH,0,0) or die "Seeking: $!";
print FH OARRAY or die "Printing: $!";
truncate(FH,tell(FH)) or die "Truncating: $!";
close(FH) or die "Closing; $!";
Комментарий
Как сказано во введении, операционная система интерпретирует файлы как неструктурированные потоки байтов. Из-за этого вставка, непосредственная модификация или изменение отдельных битов невозможны (кроме особого случая, рассматриваемого в рецепте 8.13 - файлов с записями фиксированной длины). Для хранения промежуточных данных можно воспользоваться временным файлом. Другой вариант - прочитать файл в память, модифицировать его и записать обратно.
Чтение в память всего содержимого подходит для небольших файлов, но с большими возникают сложности. Попытка применить его для 800-мегабайтных файлов журналов на Web-сервере приведет либо к переполнению виртуальной памяти, либо общему сбою системы виртуальной памяти вашего компьютера. Однако для файлов малого объема подойдет такое решение:
open(F, "+< $infile") or die "can't read $infile: $!";
$out = '';
while () {
s/DATE/localtime/eg;
$out .= $_, } seek(f, 0, 0) or die "seeking: $!";
print F $out or die "Printing: $!";
truncate(F, tell(F)) or die "Truncating: $!";
close(F) or die "Closing: $!";
Другие примеры операций, которые могут выполняться на месте, приведены и рецептах главы 8.
Этот вариант подходит лишь для самых решительных. Он сложен в написании, расходует больше памяти (теоретически - намного больше), не сохраняет резервной копии и может озадачить других программистов, которые попытаются читать данные из обновляемого файла. Как правило, он не оправдывает затраченных усилий.
Если вы особо мнительны, не забудьте заблокировать файл.
Смотри также: Описание функций seek, truncate, open и sysopen в perlfunc(1); рецепты 7.8-7.9.
Проблема
Несколько процессов одновременно пытаются обновить один и тот же файл.
Решение
Организуйте условную блокировку с помощью функции flock:
open(FH, "+< $path") or die "can't open $path: $!";
flock(FH,2) or die "can't flock $path: $!";
# Обновить файл, затем... close(FH)
or die "can't close $path: $!";
Комментарий
Операционные системы сильно отличаются по типу и степени надежности используемых механизмов блокировки. Perl старается предоставить программисту рабочее решение даже в том случае, если операционная система использует другой базовый механизм. Функция flock получает два аргумента: файловый манипулятор и число, определяющее возможные действия с данным манипулятором. Числа обычно представлены символьными константами типа LOCK_EX, имена которых можно получить из модуля Fcnti или IO::File.
Символические константы LOCK_SH, LOCK_EX, LOCK_UN и LOCK_NB появились в модуле Fcnti лишь начиная с версии 5.004, но даже теперь они доступны лишь по специальному запросу с тегом : flock. Они равны соответственно 1, 2, 4 и 8, и эти значения можно использовать вместо символических констант. Нередко встречается следующая запись:
sub LOCK_SH() { 1 } # Совместная блокировка (для чтения)
sub LOCK_EX() { 2 } # Монопольная блокировка (для записи)
sub LOCK_NB() { 4 } # Асинхронный запрос блокировки
sub LOCK_UN() { 8 } # Снятие блокировки (осторожно!)
Блокировки делятся на две категории: совместные (shared) и монопольные (exclusive). Термин "монопольный" может ввести вас в заблуждение, поскольку процессы не обязаны соблюдать блокировку файлов. Иногда говорят, что flock реализует условную блокировку, чтобы операционная система могла приостано-
вить все операции записи в файл до того момента, когда с ним закончит работу последний процесс чтения.
Условная блокировка напоминает светофор на перекрестке. Светофор работает лишь в том случае, если люди обращают внимание на цвет сигнала: красный или зеленый - или желтый для условной блокировки. Красный цвет не останавливает движение; он всего лишь сообщает, что движение следует прекратить. Отчаянный, невежественный или просто наглый водитель проедет через перекресток независимо от сигнала светофора. Аналогично работает и функция flock - она тоже блокирует другие вызовы flock, а не процессы, выполняющие ввод/вывод. Правила должны соблюдаться всеми, иначе могут произойти (и непременно произойдут) несчастные случаи.
Добропорядочный процесс сообщает о своем намерении прочитать данные из файла, запрашивая блокировку LOCK_SH. Совместная блокировка файла может быть установлена сразу несколькими процессами, поскольку они (предположительно) не будут изменять данные. Если процесс собирается произвести запись в файл, он должен запросить монопольную блокировку с помощью 1_ОСК_ЕХ. Затем операционная система приостанавливает этот процесс до снятия блокировок остальными процессами, после чего приостановленный процесс получает блокировку и продолжает работу. Можно быть уверенным в том, что на время сохранения блокировки никакой другой процесс не сможет выполнить flock(FH, LOCK_EX) для того же файла. Это похоже на другое утверждение - "в любой момент для файла может быть установлена лишь одна монопольная блокировка", но не совсем эквивалентно ему. В некоторых системах дочерние процессы, созданные функцией fork, наследуют от своих родителей не только открытые файлы, но и установленные блокировки. Следовательно, при наличии монопольной блокировки и вызове fork без ехес производный процесс может унаследовать монопольную блокировку файла.
Функция flock по умолчанию приостанавливает процесс. Указывая флаг LOCK_NB, при запросе можно получить блокировку без приостановки. Благодаря этому можно предупредить пользователя об ожидании снятия блокировок другими процессами:
unless (flock(FH, LOCK_EX|LOCK_NB)) {
warn "can't immediately write-lock the file ($!), blocking ...";
unless (flock(FH, LOCK_EX)) {
die "can't get write-lock on numfile: $!"; }
}
Если при использовании LOCK_NB вам было отказано в совместной блокировке, следовательно, кто-то другой получил LOCK_EX и обновляет файл. Отказ в монопольной блокировке означает, что другой процесс установил совместную или монопольную блокировку, поэтому пытаться обновлять файл не следует.
Блокировки исчезают с закрытием файла, что может произойти лишь после завершения процесса. Ручное снятие блокировки без закрытия файла - дело рискованное. Это связано с буферизацией. Если между снятием блокировки и очисткой буфера проходит некоторое время, то данные, заменяемые содержимым буфера, могут быть прочитаны другим процессом. Более надежный путь выглядит так:
if ($] < 5.004) { # Проверить версию Perl
my $old_fh = select(FH);
local $|=1; # Разрешить буферизацию команд
local $\ = ''; # Очистить разделитель выходных записей
print ""; # Вызвать очистку буфера
select($old_fh); # Восстановить предыдущий манипулятор
}
flock(FH, LOCK_UN);
До появления Perl версии 5.004 очистку буфера приходилось выполнять принудительно. Программисты часто забывали об этом, поэтому в 5.004 снятие блокировки изменилось так, чтобы несохраненные буферы очищались непосредственно перед снятием блокировки.
А вот как увеличить число в файле с применением flock:
use Fcnti qw(:DEFAULT :flock);
sysopen(FH, "numfile", 0_RDWR|0_CREAT)
or die "can't open numfile: $!";
flock(FH, LOCK_EX) or die "can't write-lock numfile: $!";
# Блокировка получена, можно выполнять ввод/вывод
$num = || 0; # HE ИСПОЛЬЗУЙТЕ "or" ! !
seek(FH, 0, 0) or die "can't rewind numfile : $!";
truncate(FH, 0) or die "can't truncate numfile: $!";
print FH $num+1, "\n" or die "can't write numfile: $!";
close(FH) or die "can't close numfile: $!":
Закрытие файлового манипулятора приводит к очистке буферов и снятию блокировки с файла. Функция truncate описана в главе 8.
С блокировкой файлов дело обстоит сложнее, чем можно подумать - и чем нам хотелось бы. Блокировка имеет условный характер, поэтому если один процесс использует ее, а другой - нет, все идет прахом. Никогда не используйте факт существования файла в качестве признака блокировки, поскольку между проверкой существования и созданием файла может произойти вмешательство извне. Более того, блокировка файлов подразумевает концепцию состояния и потому не соответствует моделям некоторых сетевых 4зайловых систем - например, NFS. Хотя некоторые разработчики утверждают, что fcnti решает эти проблемы, практический опыт говорит об обратном.
В блокировках NFS участвует как сервер, так и клиент. Соответственно, нам не известен общий механизм, гарантирующий надежную блокировку в NFS. Это возможно в том случае, если некоторые операции заведомо имеют атомарный характер в реализации сервера или клиента. Это возможно, если и сервер, и клиент поддерживают flock или fcnti; большинство не поддерживает. На практике вам не удастся написать код, работающий в любой системе.
Не путайте функцию Perl flock с функцией SysV lockf. В отличие от lockf flock блокирует сразу весь файл. Perl не обладает непосредственной поддержкой lockf. Чтобы заблокировать часть файла, необходимо использовать функцию fcnti (см. программу lockarea в конце главы).
Смотри также: Описание функций flock и fcnti в perlfunc(1) документация по стандартным модулям Fcnti и DB_File; рецепт 7.21-7.22.
Проблема
Операция вывода через файловый манипулятор выполняется не сразу. Из-за этого могут возникнуть проблемы в сценариях CGI на некоторых Web-серверах, враждебных по отношению к программисту. Если Web-сервер получит предупреждение от Perl до того, как увидит (буферизованный) вывод вашего сценария, он передает броузеру малосодержательное сообщение 500 Server Error. Проблемы буферизации возникают при одновременном доступе к файлам со стороны нескольких программ и при взаимодействии с устройствами или сокетами.
Решение
Запретите бусреризацию, присвоив истинное значение (обычно 1) переменной $ | на уровне файлового манипулятора:
$old_fh = select(output_handle);
$1 = 1;
select($old_fh);
Или, если вас не пугают последствия, вообще запретите буферизацию вызовом метода autoflush из модулей 10:
use IO::Handle;
OUTPUT_HANDLE->autoflush( 1);
Комментарий
В большинстве реализации stdio буферизация определяется типом выходного устройства. Для дисковых файлов применяется блочная буферизация с размером буфера, превышающим 2 Кб. Для каналов (pipes) и сокетов часто при меняется буфер размера от 0,5 до 2 Кб. Последовательные устройства, к числ\ которых относятся терминалы, модемы, мыши и джойстики, обычно буферизуются построчно; stdio передает всю строку лишь при получении перевода строки.
Функция Perl print не поддерживает по-настоящему небуферизованного вывода - физической записи каждого отдельного символа. Вместо этого поддерживается командная буферизация, при которой физическая запись выполняется после каждой отдельной команды вывода. По сравнению с полным отсутствием буферизации обеспечивается более высокое быстродействие, при этом выходные данные получаются сразу же после вывода.
Для управления буферизацией вывода используется специальная переменная $|. Присваивая ей true, вы тем самым разрешаете командную буферизацию.
На ввод она не влияет (небуферизованный ввод рассматривается в рецептах 15.6 и 15.8). Если $| присваивается false, будет использоваться стандартная буферизация stdio. Отличия продемонстрированы в примере 7.6.
Пример 7.6. seeme
#!/usr/bin/perl -w
# seeme - буферизация вывода в stdio
$| = (@argv > 0); # Командная буферизация при наличии аргументов
print "Now you don't see it...";
sleep 2;
print "now you do\n";
Если программа запускается без аргументов, STDOUT не использует командную буферизацию. Терминал (консоль, окно, сеанс telnet и т. д.) получит вывод лишь после завершения всей строки, поэтому вы ничего не увидите в течение 2 секунд, после чего будет выведена полная строка "Now you don't see it...now you do".
В сомнительном стремлении к компактности кода программисты включают возвращаемое значение select (файловый манипулятор, который был выбран в настоящий Момент) в другой вызов select:
select((select(OUTPuT_HANDLE), $| = 1)[0]);
Существует и другой выход. Модули FileHandle и 10 содержат метод autoflush. Его вызов с аргументом true или false (по умолчанию используется true) управляет автоматической очисткой буфера для конкретного выходного манипулятора:
use FileHandle;
STDERR->autoflush; # Уже небуферизован в stdio
$filehandle->autoflush(0);
Если вас не пугают странности косвенной записи (см. главу 13 "Классы, объекты и связи"), можно написать нечто похожее на обычный английский текст:
use IO::Handle;
# REMOTE_CONN - манипулятор интерактивного сокета,
# a DISK_FILE - манипулятор обычного файла.
autoflush REMOTE_CONN 1; # Отказаться от буферизации для ясности
autoflush DISK_FILE 0; # Буферизовать для повышения быстродействия
Мы избегаем жутких конструкций select, и программа становится более понятной. К сожалению, при этом увеличивается время компиляции, поскольку включение модуля IO::Handle требует чтения и компиляции тысяч строк кода. Научитесь напрямую работать с $ |, этого будет вполне достаточно.
Чтобы выходные данные оказались в нужном месте в нужное время, необходимо позаботиться о своевременной очистке буфера. Это особенно важно для соке-тов, каналов и устройств, поскольку они нередко участвуют в интерактивном вводе/выводе, а также из-за того, что вы не сможете полагаться на построчную буферизацию. Рассмотрим программу из примера 7.7.
Пример 7.7. getcomidx
#!/usr/bin/perl
# getpcomidx - получить документ index.html с www.perl.com
use IO::Socket;
$sock = new IO::socket::inet (peeraddr => 'www.perl.com', PeerPort => 'http(80)');
die "Couldn't create socket: $@>" unless $sock;
# Библиотека не поддерживает $!; в ней используется $@
$sock->autoflush(1);
# На Mac \n\n "обязательно* заменяется последовательностью \015\012\015\012.
# Спецификация рекомендует это и для других систем,
# однако в реализациях рекомендуется поддерживать и "\cJ\cJ".
# Наш опыт показывает, что именно так и получается.
$sock->print("GET /index.html http/1.1\n\n");
$document = join('', $sock->getlines());
print "DOC IS: $document\n";
Ни один из рассмотренных нами типов буферизации не позволяет управлять буферизацией ввода. Для этого обращайтесь к рецептам 15.6 и 15.8.
Смотри также: Описание переменной $ | в perlvar(1); описание функции select в perlfunc(1) документация по стандартным модулям FileHandle и IO::Handle.
Проблема
Вы хотите узнавать о наличии данных для чтения, вместо того чтобы приостанавливать процесс в ожидании ввода, как это делает о. Такая возможность пригодится при получении данных от каналов, сокетов, устройств и других программ.
Решение
Если вас не смущают операции с битовыми векторами, представляющими наборы файловых дескрипторов, воспользуйтесь функцией select с нулевым тайм аутом:
$rin = ' o;
# Следующая строка повторяется для всех опрашиваемых манипуляторов
vec($rin, fileno(FH-l), 1) = 1:
vec($rin, fileno(FH2), 1) = 1;
vec($rin, fileno(FH3), 1) = 1;
$nfound = select($rout=$rin, undef, undef, 0);
if ($nfound) {
# На одном или нескольких манипуляторах имеются входные данные
if (vec($r,fileno(FH1),1)) {
# Сделать что-то с FH1 } if (vec($r,fileno(FH2),1)) {
it Сделать что-то с FH2 } if (vec($r,fileno(FH3),1)) {
# Сделать что-то с FH3
}
}
Модуль IO::Select позволяет абстрагироваться от операций с битовыми векторами:
use IO::Select;
$select = IO::select->new();
# Следующая строка повторяется для всех опрашиваемых манипуляторов
$select->add(*FILEHANDLE):
if (@>ready = $select->can_read(0)) {
# Имеются данные на манипуляторах из массива
@ready }
Комментарий
Функция select в действительности объединяет сразу две функции. Вызванная с одним аргументом, она изменяет текущий манипулятор вывода по умолчанию (см. рецепт 7.12). При вызове с четырьмя аргументами она сообщает, какие файловые манипуляторы имеют входные данные или готовы получить вывод. В данном рецепте рассматривается только 4-аргументный вариант select.
Первые три аргумента select представляют собой строки, содержащие битовые векторы. Они определяют состояние файловых дескрипторов, ожидающих ввода, вывода или сообщений об ошибках (например, сведений о выходе данных за пределы диапазона для срочной передачи сокету). Четвертый аргумент определяет тайм-аут - интервал, в течение которого select ожидает изменения состояния. Нулевой тайм-аут означает немедленный опрос. Тайм-аут также равен вещественному числу секунд или undef. В последнем варианте select ждет, пока состояние изменится:
$rin = o o;
vec($rin, fileno(FILEHANDLE), 1) = 1;
$nfound = select($rin, undef, undef, 0); # Обычная проверка
if ($nfound) {
$line = ;
print "I read $line";
}
Однако такое решение не идеально. Если среди передаваемых символов не встретится символ перевода строки, программа переходит в ожидание в .
Чтобы справиться с этой проблемой, мы последовательно читаем по одному символу и обрабатываем готовую строку при получении "\п". При этом отпадает необходимость в синхронном вызове . Другое решение (без проверки файлов) описано в рецепте 7.15.
Модуль IO::Select скрывает от вас операции с битовыми векторами. Метод 10: : Select ->new() возвращает новый объект, для которого можно вызвать метод add, чтобы дополнить набор новыми файловыми манипуляторами. После включениях всех интересующих вас манипуляторов вызываются функции сап_геаа, can_write и can_exception. Функции возвращают список манипуляторов, ожидающих чтения, записи или непрочитанных срочных данных (например, информации о нарушении диапазона TCP).
Вызовы 4-аргументной версии select не должны чередоваться с вызовами каких-либо функций буферизованного вывода, перечисленных во введении (read, о, seek, tell и т. д.). Вместо этого следует использовать sys read - вместе с sysseek, если вы хотите изменить позицию внутри файла для данного манипулятора.
Чтение данных из сокета или канала с немедленным продолжением работы описано в рецепте 17.13. Асинхронному чтению с терминала посвящены рецепты 15.6 и 15.8.
Смотри также: Описание функции select в perlfunc(1); документация по стандартному модулю IO::Select; рецепт 7.14.