2011-06-09 26 views
24

Quiero hacer un script rápido que escriba en un archivo si se proporciona un archivo, o stdout si no se proporciona ningún archivo. Sería mucho más fácil para mí hacer esto si pudiera iniciar la secuencia de comandos apuntando un nuevo manejador de archivo, OUTFILE, a cualquiera de estos que sea apropiado.¿Cómo señalo un manejador de archivo a STDOUT (u otro manejador de archivo) en Perl?

Sé que puedo lograr lo que quiero simplemente apuntando STDOUT a mi archivo de salida cuando sea apropiado, pero prefiero no hacerlo porque no quiero confundir a nadie que use el script más tarde y me pregunto por qué sus declaraciones impresas no funciona Además, aún me gustaría usar la declaración impresa ocasional para fines de resolución de problemas, y nunca quiero esta impresión en el archivo de salida.

Dicho todo esto, lo que estoy buscando es algo a lo largo de las líneas de:
open(OUTFILE, ($output ? ">$output" : STDOUT);
excepto que no funciona.

Ideas?

Respuesta

37

Algunas formas (globos):

# Requires 2-arg open, unfortunately 
open(OUTPUT, "> ".($output || '-')) or die; 

if ($output) { 
    open(OUTPUT, '>', $output) or die; 
} else { 
    *OUTPUT = *STDOUT; 
} 

if ($output) { 
    open(OUTPUT, '>', $output) or die; 
} else { 
    open(OUTPUT, '>&', \*STDOUT) or die; 
} 

Algunas formas (var léxica):

# Requires 2-arg open, unfortunately 
open(my $fh, "> ".($output || '-')) or die; 

my $fh; 
if ($output) { 
    open($fh, '>', $output) or die; 
} else { 
    $fh = \*STDOUT; 
} 

my $fh; 
if ($output) { 
    open($fh, '>', $output) or die; 
} else { 
    open($fh, '>&', \*STDOUT) or die; 
} 

Algunas formas (otros):

# Changes the default handle for <print "foo">. 
if ($output) { 
    open(OUTPUT, '>', $output) or die; 
    select(OUTPUT); 
} 
+2

'...} else {open ($ fh, '> &'. Fileno (STDOUT)); } ' – mob

3

Siempre puede escribir la salida normal en STDOUT y sus declaraciones de depuración en STDERR. Entonces, si el usuario quiere que la salida vaya a un archivo, puede simplemente redirigir STDOUT al archivo en la línea de comando.

+0

Como he dicho, sé que puedo redirigir el archivo de salida estándar, pero esto es realmente algo que preferiría evitar hacer tan Me resulta confuso y molesto cuando otros lo hacen y tengo que lidiar con el código más tarde. Funcionará como un último esfuerzo, pero preferiría simplemente dirigir un manejador de archivo diferente a un archivo de salida o STDOUT ya que creo que esto sería mucho más limpio. – Eli

+3

@Eli - No tienes que hacerlo en tu código. Simplemente escriba su script para imprimir siempre en STDOUT. Cuando un usuario ejecuta el script normalmente verá el resultado en la pantalla. Pero también pueden hacer 'scriptname.pl 'filename.txt' en cuyo caso se ejecutará el script y la salida terminará en el archivo' filename.txt'. Cualquier código de depuración en STDERR seguirá apareciendo en la consola. Esta es la forma en que funcionan la mayoría de las utilidades y scripts de * nix. –

+3

@Eli - Este es el enfoque más simple en todos los aspectos. No tiene ningún código en su script que conozca las diferentes rutas de salida. Todo lo que haces en el código es escribir en STDOUT. El usuario decide cuándo utiliza el script, si quiere ver el resultado en la consola, o ponerlo en un archivo, o incluso canalizarlo en el STDIN de otro comando. –

4

Nevermind. Lo averigué. Lanzar un ampersand hace el truco. El código de trabajo completo es:
open(OUTPUT, ($output ? ">$output" : ">&STDOUT"));

+3

Lo que haría que esta respuesta sea genial es una explicación de cómo funciona esto. – Ether

7

- es mágico. Sigue la convención o violas el principio de menor sorpresa.

use autodie qw(:all); 
use Getopt::Long qw(GetOptions); 
GetOptions(\my %opt, 'out=s') or die 'usage'; 
die 'usage' unless $opt{out}; 
open my $handle, ">$opt{out}"; 

perl foo -o blahblah  # opens file blahblah for writing 
perl foo -o -    # goes to STDOUT 

relacionadas: Are there reasons to ever use the two-argument form of open(...) in Perl?

10

Por el bien del lector (desde OP ya "tengo trabajo"):

La documentación de Perl, "perlopentut" (perldoc perlopentut) proporciona ejemplos de cómo abrir un identificador de archivo alternativo dirigido a STDOUT, pero utiliza manejadores de archivos sin bordes y la versión de dos arg de open, ambos algo antiguos. El libro "Perl Best Practices" de Damian Conway (así como Perl :: Critic, que está basado en el libro de Damian) enfatiza el uso de tres arg opens y manejadores de archivos léxicos (my $foo style filehandles). El "Perl Moderno" de chromatic también menciona brevemente el uso de manejadores de archivos léxicos, y desalienta las palabras no tratadas cuando es práctico. Los tres arg open se consideran una forma más segura, ya que limitan la exposición a las inyecciones de proyectiles. Si bien el caso específico aquí es de bajo riesgo, sigue siendo un buen hábito el incumplimiento de las tres arg open.

Se necesita una cuidadosa lectura de los ejemplos que se muestran en la documentación de Perl open (perldoc -f abierto), y un poco de interpretación para notar la colocación correcta del carácter y comercial en la apertura de una filahandle duplicado dirigida a STDOUT cuando se utilizan los tres-arg versión de open. Uno podría suponer erróneamente que esto debería funcionar: open my $fh, '>', '&STDOUT' or die $!;, sobre todo porque se ve como un formato que estamos acostumbrados a ver (símbolo y precediendo al nombre de un símbolo). Pero esa sintaxis no es lo que open necesita, y estamos acostumbrados a verlo en un contexto completamente diferente: ciertas llamadas de subrutinas.

La forma correcta de abrir una víctima a STDOUT con los tres arg open es combinar la > y la & en el mismo segundo argumento, y dejar STDOUT al descubierto, como este:

use Modern::Perl; 

open my $fh, '>&', STDOUT or die "Bleah: $!"; 

say $fh 'Printing alternate filehandle "$fh" worked.'; 
say 'Implicitly printing to "STDOUT" also works.'; 

O pasar STDOUT a open como una referencia a la typeglob:

open my $fh, '>&', \*STDOUT or ... 
Cuestiones relacionadas