2010-02-22 4 views
5

Si j'ai une fonction qui pourrait être passée un nom de fichier ou plusieurs handles de fichiers ou typeglobs, comment la fonction peut-elle distinguer parmi ces arguments - y compris la différence, par exemple, entre *DATA et *STDIN?Comment un sous-programme Perl peut-il faire la distinction entre les noms de fichiers, les fichiers, * DATA et * STDIN?

Code mis à jour, basé sur les réponses reçues jusqu'ici Merci, tout le monde.

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

Ce sont toutes des poignées de fichiers. Qu'est-ce que vous essayez exactement de tester? Vous pouvez utiliser le test "-t" sur la poignée pour vérifier si elle provient de/va vers un terminal (TTY), ce qui sera normalement vrai pour STDIN et STDOUT, à moins d'être acheminé. – amphetamachine

+0

S'il vous plaît laissez-nous savoir le contexte plus large de ce que vous voulez faire.Pourquoi devez-vous être capable de distinguer entre 'DATA' et' STDIN'? –

+1

@gbacon En toute honnêteté, je ne suis pas certain. Je travaillais sur quelque chose en retard la nuit dernière et je pensais que cela pourrait aider à être capable de distinguer. Puis j'ai remarqué que Data :: Dumper était capable de les différencier (sorte de), donc j'ai pensé que la question pourrait avoir une réponse simple et je l'ai posée sur SO. Depuis lors, ma réflexion sur mon projet a évolué, alors maintenant nous sommes juste à la curiosité. :) – FMc

Répondre

2

Mise à jour: Le problème de la distinction entre une variable qui pourrait être attribué aux *DATA ou *STDIN Globs est un travail pour 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 

Si $f est un handle de fichier, puis ref $f ou ref \$f seraSi $f est un scalaire, alors ref \$f sera "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 
+0

sub is_filehandle { devrait être sub filehandle_or_scalar { –

1

Vous pouvez utiliser filtrage sur les descripteurs de fichiers pour stringafied * STDIN, * DATA, etc ...

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

Hacky, mais peut être assez ...

1

approche de mobrule regards prometteur:

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

affichera GLOB. Cependant, il en sera

perl -E 'say ref \*FOO;' 

Un descripteur de fichier « réel » aura également un descripteur de fichier associé à ce que vous pouvez déterminer en utilisant fileno:

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

va afficher quelque chose comme:

$VAR1 = [ 
      3, 
      0, 
      undef 
     ]; 

Vous pouvez utiliser ceci pour indiquer à un GLOB utilisé pour l'E/S de fichier de celui qui ne l'est pas. Sur les systèmes UNIX, le flux d'entrée standard est associé au descripteur de fichier par convention.

Une autre chose qui vient à l'esprit est une classe liée à un descripteur de fichier . Ceux-ci doivent implémenter une interface particulière que vous pouvez tester avec en utilisant can. Voir le cravate VARIABLE, CLASSNAME, LIST entrée dans perlfunc pour plus de détails sur cette interface.

Questions connexes