2010-02-22 6 views
5

Se si dispone di una funzione che può essere passata a un nome file oa vari handle di file o typeglobs, in che modo la funzione può distinguere tra questi argomenti, incluso il rilevamento della differenza, ad esempio tra *DATA e *STDIN?Come può una subroutine Perl distinguere tra nomi di file, file handes, * DATA e * STDIN?

Codice aggiornato, basato sulle risposte ricevute finora Grazie a tutti.

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

Sono tutti gli handle di file. Che cosa stai cercando di testare esattamente? È possibile utilizzare il test "-t" sull'impugnatura per verificare se proviene da/che va a un terminale (TTY), che normalmente sarà vero per STDIN e STDOUT, a meno che non sia collegato. – amphetamachine

+0

Fateci sapere il contesto più ampio di ciò che volete fare.Perché è necessario essere in grado di distinguere tra 'DATA' e' STDIN'? –

+1

@ gbacon In tutta onestà, non ne sono sicuro. Stavo lavorando a qualcosa ieri sera e ho pensato che potesse essere d'aiuto riuscire a distinguere. Poi ho notato che Data :: Dumper era in grado di distinguerli (una specie di), quindi ho pensato che la domanda potesse avere una risposta semplice e l'ho posta su SO. Da allora, il mio pensiero sul mio progetto si è evoluto, quindi ora siamo solo alla curiosità. :) – FMc

risposta

2

Update: Il problema della distinzione tra una variabile che potrebbe essere assegnato ai *DATA o *STDIN gocce è un lavoro per 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 

Se $f è un filehandle, quindi sia ref $f o ref \$f sarà "GLOB" Se $f è uno scalare, quindi ref \$f sarà "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 { dovrebbe essere sub filehandle_or_scalar { –

1

Si potrebbe utilizzare pattern matching sui filehandle stringafied per * STDIN, * DATI, ecc ...

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

Hacky, ma può essere sufficiente ... sguardi approccio

1

di mobrule promettente:

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

emetterà GLOB. Tuttavia, così sarà

perl -E 'say ref \*FOO;' 

Un filehandle "reale" avrà anche un descrittore di file associato esso che è possibile determinare con fileno:

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

emetterà qualcosa come:

$VAR1 = [ 
      3, 
      0, 
      undef 
     ]; 

Si può usare questo per dire a un GLOB che viene utilizzato per l'I/O di file da uno che non lo è. Sui sistemi UNIX, il flusso di input standard è associato al descrittore di file per convenzione.

Un'altra cosa che viene in mente è una classe che è legata a un filehandle . Questi devono implementare una particolare interfaccia che puoi provare con per l'utilizzo di can. Vedere il legame VARIABILE, NOME CATEGORIA, ELENCO voce in perlfunc per dettagli su questa interfaccia.