2010-02-22 6 views
5

Wenn ich eine Funktion habe, der ein Dateiname oder verschiedene Dateihandles oder typeglobs übergeben werden könnten, wie kann die Funktion zwischen diesen Argumenten unterscheiden - einschließlich der Unterscheidung zwischen zB *DATA und *STDIN?Wie kann eine Perl-Subroutine zwischen Dateinamen, Dateihandeln, * DATA und * STDIN unterscheiden?

Aktualisierter Code, basierend auf bisher erhaltenen Antworten Danke, alle.

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__ 
+1

Sie sind alle Dateigriffe. Was genau willst du testen? Sie können den "-t" -Test auf dem Handle verwenden, um zu überprüfen, ob es von einem Terminal (TTY) kommt oder zu ihm geht, was normalerweise für STDIN und STDOUT gilt, sofern nicht piped. – amphetamachine

+0

Bitte teilen Sie uns den weiteren Kontext dessen, was Sie tun möchten.Warum müssen Sie zwischen "DATA" und "STDIN" unterscheiden können? –

+1

@gbacon Ehrlich gesagt bin ich mir nicht sicher. Ich habe letzte Nacht an etwas spät gearbeitet und dachte, es könnte helfen, etwas unterscheiden zu können. Dann fiel mir auf, dass Data :: Dumper in der Lage war, sie (irgendwie) voneinander zu unterscheiden, also dachte ich, die Frage könnte eine einfache Antwort haben und ich stellte sie auf SO. Seither hat sich mein Denken über mein Projekt weiterentwickelt, und jetzt sind wir nur noch neugierig. :) – FMc

Antwort

2

Update: Das Problem zwischen einer Variablen unterscheiden, die den *DATA oder *STDIN Klackse zugeordnet werden könnte, ist eine Aufgabe für 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 

Wenn $f ist ein Dateihandle, dann entweder ref $f oder ref \$f wirdsein Wenn $f ein Skalar ist, dann wird ref \$f"SCALAR" sein.

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 
+0

sub is_filehandle { sollte sein sub filehandle_or_scalar { –

1

Sie Muster auf den stringafied Dateihandies für * STDIN, * DATA passende verwenden könnte, etc ...

if ($f =~ /\bSTDIN$/) { 
    return "STDIN"; 
} elsif ($f =~ /\bDATA$/) { 
    return "DATA"; 
} 

Hacky, kann aber genug sein ...

1

mobrule die Blicke Ansatz Vielversprechend:

perl -E 'open $fh, "<", "/dev/null"; say ref $fh;' 

wird ausgegeben GLOB. Doch so wird

perl -E 'say ref \*FOO;' 

Eine "echte" Dateihandle wird auch ein Dateideskriptor mit es dem zugehörigen haben Sie bestimmen können, fileno mit:

perl -MData::Dumper -E 'open $fh, "<", "/dev/null"; say Data::Dumper::Dumper([fileno $fh, fileno \*STDIN, fileno \*FOO])' 

ausgeben wird so etwas wie:

$VAR1 = [ 
      3, 
      0, 
      undef 
     ]; 

Sie können damit einen GLOB angeben, der für Datei-E/A verwendet wird, von . Auf UNIX-Systemen ist der Standardeingabestrom per Konvention dem Dateideskriptor 0 zugeordnet.

Eine andere Sache, die in den Sinn kommt, ist eine Klasse, die an ein Dateihandle gebunden ist. Diese müssen eine bestimmte Schnittstelle implementieren, die Sie für die Verwendung von can testen können. Siehe tie VARIABLE, CLASSNAME, LIST Eintrag in perlfunc für Details über diese Schnittstelle.