В старые добрые времена, предшествующие Perl v5.6, который ввел лексические файловые дескрипторы - более чем десять лет назад - передача дескрипторов файлов и каталогов была неудобной. Код из вашего вопроса написан с использованием этого старомодного стиля.
Техническое имя для *STDIN
, например, это typeglob , объясненное в разделе «Typeglobs and Filehandles» perldata . Вы можете столкнуться с манипуляциями с typeglobs для различных целей в унаследованном коде. Обратите внимание, что вы можете захватывать только типы глобальных переменных, но не лексические.
Передача дескрипторов была обычным делом для непосредственного обращения с типоглобами, но были и другие применения. Подробнее см. Ниже.
- Передача файловых дескрипторов подпрограммам
- Синтаксическая неоднозначность: строка или дескриптор файла
- Псевдонимы с помощью назначения typeglob
- Локализация ручек с помощью локализации typeglobs
- заглядывать под капот:
*foo{THING}
синтаксис
- Свяжите все это вместе: DWIM!
Передача файловых дескрипторов подпрограммам
В документации perldata объясняется :
Typeglobs и Filehandles
Perl использует внутренний тип, называемый typeglob , для хранения всей записи таблицы символов. Префикс типа для typeglob - *
, потому что он представляет все типы. Раньше это был предпочтительный способ передачи массивов и хэшей по ссылке в функцию, но теперь, когда у нас есть реальные ссылки, это редко требуется.
[...]
Другое использование для typeglobs - передача файловых дескрипторов в функцию или создание новых файловых дескрипторов. Если вам нужно использовать typeglob для сохранения дескриптора файла, сделайте это следующим образом:
$fh = *STDOUT;
или, возможно, в качестве реальной ссылки, например:
$fh = \*STDOUT;
См. perlsub для примеров использования их в качестве косвенных файловых дескрипторов в функциях.
Секция , на которую ссылается perlsub , находится ниже.
Проходные записи в таблице символов (typeglobs)
ПРЕДУПРЕЖДЕНИЕ: Механизм, описанный в этом разделе, первоначально был единственным способом имитации передачи по ссылке в более старых версиях Perl. Хотя в современных версиях он по-прежнему работает нормально, с новым справочным механизмом, как правило, легче работать. Смотри ниже.
Иногда вам не нужно передавать значение массива в подпрограмму, а скорее его имя, чтобы подпрограмма могла изменять глобальную копию, а не работать с локальной копией. В Perl вы можете ссылаться на все объекты с определенным именем, поставив перед именем звездочку: *foo
. Это часто называют «typeglob», потому что звезду на передней панели можно рассматривать как подстановочный знак для всех смешных префиксных символов в переменных и подпрограммах и тому подобном.
При оценке typeglob создает скалярное значение, которое представляет все объекты с таким именем, включая любой дескриптор файла, формат или подпрограмму. При назначении, это приводит к тому, что упомянутое имя ссылается на любое значение *
, присвоенное ему. [...]
Обратите внимание, что типоблок можно использовать только для глобальных переменных, но не для лексических. Обратите внимание на предупреждение выше. Предпочитаю избегать этой неясной техники.
Синтаксическая неоднозначность: строка или дескриптор файла?
Без символа *
голое слово - это просто строка.
Простых струн иногда достаточно, но. Например, оператор print
позволяет
$ perl -le 'print { "STDOUT" } "Hiya!"'
Hiya!
$ perl -le '$h="STDOUT"; print $h "Hiya!"'
Hiya!
$ perl -le 'print "STDOUT" +123'
123
Сбой при включенном strict 'refs'
. Руководство объясняет:
FILEHANDLE может быть именем скалярной переменной, в этом случае переменная содержит имя или ссылку на дескриптор файла, таким образом вводя один уровень косвенности.
В вашем примере рассмотрим синтаксическую неоднозначность. Без символа *
вы могли бы иметь в виду строки
$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', 'OP_HANDLE');
или, возможно, дополнительный вызов
$ perl -MO=Deparse,-p prog.pl
use JavaScript::Minifier;
sub OP_HANDLE {
1;
}
(my $obj = 'JavaScript::Minifier'->new);
$obj->minify('IP_HANDLE', OP_HANDLE());
или снаш, файловый дескриптор. Обратите внимание, что в приведенных выше примерах голое слово JavaScript::Minifier
также компилируется как простая строка.
Включите прагму strict
, и все равно это все исчезнет:
$ perl -Mstrict prog.pl
Bareword "IP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.
Bareword "OP_HANDLE" not allowed while "strict subs" in use at prog.pl line 6.
Псевдонимы с помощью назначения typeglob
Один трюк с typeglobs, который удобен для сообщений переполнения стека, это
*ARGV = *DATA;
(Я мог бы быть более точным с *ARGV = *DATA{IO}
, но это немного суетливо.)
Это позволяет оператору алмаза <>
читать из файлового дескриптора DATA
, как в
#! /usr/bin/perl
*ARGV = *DATA; # for demo only; remove in production
while (<>) { print }
__DATA__
Hello
there
Таким образом, программа и ее входные данные могут быть в одном файле, и код более близок к тому, как он будет выглядеть в рабочей среде: просто удалите присвоение typeglob.
Локализация ручек с помощью локализации typeglobs
Как отмечено в perlsub
Временные значения через local()
ПРЕДУПРЕЖДЕНИЕ: Как правило, вы должны использовать my
вместо local
, потому что это быстрее и безопаснее. Исключениями являются глобальные переменные пунктуации, глобальные файловые дескрипторы и форматы, а также прямое манипулирование самой таблицей символов Perl. local
чаще всего используется, когда текущее значение переменной должно быть видимым для вызываемых подпрограмм. [...]
вы можете использовать typeglobs для локализации файловых дескрипторов:
$ cat prog.pl
#! /usr/bin/perl
sub foo {
local(*STDOUT);
open STDOUT, ">", "/dev/null" or die "$0: open: $!";
print "You can't see me!\n";
}
print "Hello\n";
foo;
print "Good bye.\n";
$ ./prog.pl
Hello
Good bye.
«Когда все еще использовать local()
» в perlsub имеет другой пример.
2. Вам необходимо создать локальный дескриптор файла или каталога или локальную функцию.
Функция, которой нужен собственный файловый дескриптор, должна использовать local()
для полного typeglob. Это может быть использовано для создания новых записей таблицы символов:
sub ioqueue {
local (*READER, *WRITER); # not my!
pipe (READER, WRITER) or die "pipe: $!";
return (*READER, *WRITER);
}
($head, $tail) = ioqueue();
Чтобы подчеркнуть, этот стиль старомоден. Предпочитаю избегать глобальных файловых дескрипторов в новом коде, но полезно понимать технику в существующем коде.
Подглядывание под капотом: *foo{THING}
Синтаксис
Вы можете получить в разных частях типа глоб, как perlref объясняет:
Ссылка может быть создана с использованием специального синтаксиса, который с любовью называют синтаксисом *foo{THING}
. *foo{THING}
возвращает ссылку на слот THING в *foo
(это запись таблицы символов, которая содержит все, что называется foo).
$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
$formatref = *foo{FORMAT};
Все они говорят сами за себя, кроме *foo{IO}
. Он возвращает дескриптор ввода-вывода, используемый для дескрипторов файлов (open
), сокетов (socket
и socketpair
) и дескрипторов каталогов (opendir
). Для совместимости с предыдущими версиями Perl *foo{FILEHANDLE}
является синонимом *foo{IO}
, хотя с 5.8.0 устарел. Если предупреждения об устаревании действуют, он предупредит об их использовании.
*foo{THING}
возвращает undef
, если эта конкретная вещь еще не использовалась, за исключением скаляров. *foo{SCALAR}
возвращает ссылку на анонимный скаляр, если $foo
еще не использовался. Это может измениться в будущем выпуске.
*foo{IO}
является альтернативой механизму *HANDLE
, приведенному в [«Typeglobs and Filehandles» в perldata] для передачи файловых дескрипторов в или из подпрограмм или для хранения в более крупных структурах данных. Его недостаток в том, что он не будет создавать новый дескриптор файла для вас. Его преимущество в том, что у вас меньше риск забить больше, чем вы хотите с помощью назначения типа «глоб». (Тем не менее, он по-прежнему сопоставляет дескрипторы файлов и каталогов.) Однако, если вы присваиваете входящее значение скаляру вместо глобуса типа, как мы это делаем в примерах ниже, риска не будет.
splutter(*STDOUT); # pass the whole glob
splutter(*STDOUT{IO}); # pass both file and dir handles
sub splutter {
my $fh = shift;
print $fh "her um well a hmmm\n";
}
$rec = get_rec(*STDIN); # pass the whole glob
$rec = get_rec(*STDIN{IO}); # pass both file and dir handles
sub get_rec {
my $fh = shift;
return scalar <$fh>;
}
Свяжите все это вместе: DWIM!
Контекст является ключевым с Perl. В вашем примере, хотя синтаксис может быть неоднозначным, это не так: даже если параметры являются строками, эти строки явно предназначены для именования файловых дескрипторов.
Итак, рассмотрим все случаи minify
, возможно, потребуется обработать:
- bareword
- голый шрифт
- ссылка на typeglob
- файловая ручка в скаляре
Например:
#! /usr/bin/perl
use warnings;
use strict;
*IP_HANDLE = *DATA;
open OP_HANDLE, ">&STDOUT";
open my $fh, ">&STDOUT";
my $offset = tell DATA;
use JavaScript::Minifier;
my $obj = JavaScript::Minifier->new;
$obj->minify(*IP_HANDLE, "OP_HANDLE");
seek DATA, $offset, 0 or die "$0: seek: $!";
$obj->minify(\*IP_HANDLE, $fh);
__DATA__
Ahoy there
matey!
как библиотека autHor, будучи любезным, может быть полезным. Чтобы проиллюстрировать это, следующий фрагмент JavaScript :: Minifier понимает как старомодные, так и современные способы передачи файловых дескрипторов.
package JavaScript::Minifier;
use warnings;
use strict;
sub new { bless {} => shift }
sub minify {
my($self,$in,$out) = @_;
for ($in, $out) {
no strict 'refs';
next if ref($_) || ref(\$_) eq "GLOB";
my $pkg = caller;
$_ = *{ $pkg . "::" . $_ }{IO};
}
while (<$in>) { print $out $_ }
}
1;
Выход:
$ ./prog.pl
Name "main::OP_HANDLE" used only once: possible typo at ./prog.pl line 7.
Ahoy there
matey!
Ahoy there
matey!