2010-02-22 9 views
5

Si tengo una función que puede pasar un nombre de archivo o varios manejadores de archivo o typeglobs, ¿cómo puede la función distinguir entre estos argumentos, incluyendo decir la diferencia, por ejemplo, entre *DATA y *STDIN?¿Cómo puede distinguir una subrutina Perl entre los nombres de archivo, las herramientas de archivo, * DATA y * STDIN?

Código actualizado, basado en las respuestas recibidas hasta ahora Gracias a todos.

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

Son todos manejadores de archivo. ¿Qué estás tratando de probar exactamente? Puede usar la prueba "-t" en el mango para verificar si se trata de un terminal (TTY), lo que normalmente será cierto para STDIN y STDOUT, a menos que esté conectado por cañería. – amphetamachine

+0

Háganos saber el contexto más amplio de lo que quiere hacer.¿Por qué necesita poder distinguir entre 'DATA' y' STDIN'? –

+1

@gbacon Honestamente, no estoy seguro. Estuve trabajando en algo anoche y pensé que sería útil poder distinguir. Luego noté que Data :: Dumper podía diferenciarlos (más o menos), así que pensé que la pregunta podría tener una respuesta simple y la planteé en SO. Desde entonces, mi pensamiento acerca de mi proyecto ha evolucionado, así que ahora estamos solo por curiosidad. :) – FMc

Respuesta

2

Actualización: El problema de distinguir entre una variable que se haya asignado a los *DATA o *STDIN pegotes es un trabajo para 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 es un identificador de archivo, luego ref $f o ref \$f será "GLOB" Si $f es un escalar, entonces ref \$f será "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 { debe ser sub filehandle_or_scalar { –

1

Usted podría utilizar patrones sobre los controladores de archivo stringafied para STDIN *, * DATOS, etc ...

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

Hacky, pero puede ser suficiente ... enfoque

1

de mobrule miradas prometedor:

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

dará salida GLOB. Sin embargo, por lo que se

perl -E 'say ref \*FOO;' 

Un gestor de archivo "real" también tendrá un descriptor de fichero asociado con lo que se puede determinar utilizando fileno:

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

dará salida a algo como:

$VAR1 = [ 
      3, 
      0, 
      undef 
     ]; 

Puede usar esto para indicarle a un GLOB que se está utilizando para archivos E/S que no lo es. En sistemas UNIX, el flujo de entrada estándar está asociado con el descriptor de archivo por convención.

Otra cosa que viene a la mente es una clase que está vinculada a un identificador de archivo . Estos necesitan implementar una interfaz particular que usted puede probar usando can. Consulte el empate VARIABLE, CLASSNAME, LISTA entrada en perlfunc para obtener detalles sobre esta interfaz.

Cuestiones relacionadas