Если у меня есть функция, которая могла бы быть передана имя файла или различные дескрипторы файлов или typeglobs, как функция может различать эти аргументы - включая сообщение различия, например, между *DATA
и *STDIN
?
Обновленный код, на основе ответов, полученных до сих пор Спасибо, все.
use strict;
use warnings;
use FileHandle;
sub file_thing_type {
my ($f) = shift;
my $type;
my $r = ref $f;
if ($r eq 'GLOB' or ref(\$f) eq 'GLOB'){
# Regular and built-in file handles.
my $fn = fileno $f;
if (defined $fn){
my %built_in = (
'STDIN' => fileno(*STDIN),
'STDOUT' => fileno(*STDOUT),
'STDERR' => fileno(*STDERR),
'DATA' => fileno(*DATA),
);
for my $k (keys %built_in){
if (defined $built_in{$k} and $built_in{$k} == $fn){
$type = $k;
last;
}
}
$type = 'regular file handle' unless defined $type;
}
else {
$type = 'non-IO glob';
}
}
elsif ($r){
# A reference of some kind.
$type = $r;
# Might be an IO object. Has it been opened?
{
no warnings 'unopened';
$type .= ' opened' if -f $f;
}
}
else {
# File name or just some other value?
$type = -f $f ? 'file name' : 'other';
}
return $type;
}
open(my $h, '<', $0) or die $!;
printf "%12s => %s\n",
$_->[0],
file_thing_type($_->[1])
for (
[ 'handle', $h ], # regular file handle
[ 'DATA', *DATA ], # DATA if source has DATA section; else non-IO glob
[ 'STDIN', *STDIN ], # STDIN
[ 'STDOUT', *STDOUT ], # STDOUT
[ 'STDERR', *STDERR ], # STDERR
[ 'FOO', *FOO, *FOO ], # non-IO glob
[ 'FileHandle', FileHandle->new ], # FileHandle
[ 'FileHandle', FileHandle->new($0) ], # FileHandle opened
[ 'file name', $0 ], # file name
[ 'not file', '' ], # other
[ 'misc', {bar=>1} ], # HASH
);
__END__
Обновление: Проблема различения переменной, которая может быть присвоена глобусам *DATA
или *STDIN
- это работа для fileno
:
sub data_or_stdin { my $x = shift; if (fileno($x) == fileno(DATA)) { return "DATA"; } elsif (fileno($x) == fileno(STDIN)) { return "STDIN"; } else { return "NEITHER"; } } print "DATA: ", data_or_stdin(*DATA), "\n"; print "STDIN: ", data_or_stdin(*STDIN), "\n"; open(ZZZ, ">>", "zzz"); close ZZZ; open(ZZZ, "<", "zzz"); print "ZZZ: ", data_or_stdin(*ZZZ), "\n"; close ZZZ; open($fh, "<", "zzz"); print "\$fh=ZZZ: ", data_or_stdin($fh), "\n"; close $fh; $fh = *DATA; print "\$fh=DATA: ", data_or_stdin($fh), "\n"; $fh = *STDIN; print "\$fh=STDIN: ", data_or_stdin($fh), "\n"; __END__ stuff;
$ perl data_or_stdin.pl DATA: DATA STDIN: DATA ZZZ: NEITHER $fh=ZZZ: NEITHER $fh=DATA: DATA $fh=STDIN: DATA
Если $f
является файловым хэндлом, то либо ref $f
, либо ref \$f
будет "GLOB"
.
Если $f
- скаляр, то ref \$f
будет "SCALAR"
.
sub filehandle_or_scalar {
my $x = shift;
if (ref $x eq "GLOB" || ref \$x eq "GLOB") {
return "filehandle";
} elsif (ref \$x eq "SCALAR") {
return "scalar";
} else {
return "not filehandle or scalar";
}
}
print "STDIN: ", filehandle_or_scalar(*STDIN), "\n";
print "\$_: ", filehandle_or_scalar($_), "\n";
open($fh, ">", "zzz");
print "\$fh: ", filehandle_or_scalar($fh), "\n";
print "string: ", filehandle_or_scalar('file.txt'), "\n";
print "ref: ", filehandle_or_scalar(\$x), "\n"
###########################################
$ perl filehandle_or_scalar.pl
STDIN: filehandle
$_: scalar
$fh: filehandle
string: scalar
ref: not filehandle or scalar
Вы можете использовать сопоставление шаблонов в строковых файлах для *STDIN, *DATA и т.д....
if ($f =~ /\bSTDIN$/) {
return "STDIN";
} elsif ($f =~ /\bDATA$/) {
return "DATA";
}
Халтурно, но может быть достаточно...
mobrule выглядит многообещающим:
perl -E 'open $fh, "<", "/dev/null"; say ref $fh;'
выведет GLOB
. Однако, как и
perl -E 'say ref \*FOO;'
«настоящий» дескриптор файла также будет иметь связанный с
дескриптор файла, который вы можете определить с помощью fileno
:
perl -MData::Dumper -E 'open $fh, "<", "/dev/null"; say Data::Dumper::Dumper([fileno $fh, fileno \*STDIN, fileno \*FOO])'
выведет что-то вроде:
$VAR1 = [
3,
0,
undef
];
Вы можете использовать это, чтобы отличить GLOB, который используется для файлового ввода-вывода, от того, что не используется. В системах UNIX стандартный входной поток по соглашению связан с дескриптором файла 0 .
Еще одна вещь, которая приходит на ум, - это класс, связанный с дескриптором файла
. Они должны реализовать определенный интерфейс, который вы
можете протестировать с помощью can
. См. Запись tie VARIABLE, CLASSNAME, LIST
в perlfunc для получения подробной информации об этом интерфейсе.