Pergunta

Eu gosto de usar o recurso de perl bacana onde a leitura do <> operador ângulo vazio magicamente dá seu programa semântica filtro UNIX, mas eu gostaria de ser capaz de aceder a esta funcionalidade através de um filehandle real (ou IO :: Handle objeto, ou similar), para que eu possa fazer coisas como passá-lo em sub-rotinas e tal. Existe alguma maneira de fazer isso?

Esta questão é particularmente difícil para o Google, porque a pesquisa de "operador de ângulo" e "filehandle" só me diz como ler filehandles usando o operador ângulo.

Foi útil?

Solução

A partir perldoc perlvar :

  • ARGV

O filehandle especial que itera sobre os nomes de arquivo de linha de comando em @ARGV. Normalmente escrito como a filehandle nulo na <> operador ângulo. Note que atualmente ARGV só tem seu efeito mágico dentro do operador <>; em outros lugares, é apenas uma filehandle simples correspondente ao último arquivo aberto por <>. Em particular, passando \*ARGV como um parâmetro para uma função que espera um filehandle não pode causar a sua função para automaticamente ler o conteúdo de todos os arquivos em @ARGV.

Eu acredito que as respostas todos os aspectos da questão em que "odeio dizer isso, mas não vai fazer o que quiser" espécie de caminho. O que você poderia fazer é funções fazem que levam uma lista de nomes de arquivos para abrir e fazer isso:

sub takes_filenames (@) {
  local @ARGV = @_;
  // do stuff with <>
}

Mas isso é provavelmente o melhor que você vai ser capaz de gerir.

Outras dicas

Expandindo a idéia de Chris Lutz, aqui é uma implementação muito rudimentar:

#!/usr/bin/perl

package My::ARGV::Reader;

use strict; use warnings;
use autodie;
use IO::Handle;

use overload
    '<>' => \&reader,
    '""' => \&argv,
    '0+' => \&input_line_number,
;

sub new {
    my $class = shift;
    my $self = {
        names => [ @_ ],
        handles => [],
        current_file => 0,
    };
    bless $self => $class;
}

sub reader {
    my $self = shift;

    return scalar <STDIN> unless @{ $self->{names}};

    my $line;

    while ( 1 ) {
        my $current = $self->{current_file};
        return if $current >= @{ $self->{names} };

        my $fh = $self->{handles}->[$current];

        unless ( $fh ) {
            $self->{handles}->[$current] = $fh = $self->open_file;
        }

        if( eof $fh ) {
            close $fh;
            $self->{current_file} = $current + 1;
            next;
        }

        $line = <$fh>;
        last;
    }
    return $line;
}

sub open_file {
    my $self = shift;
    my $name = $self->{names}->[ $self->{current_file} ];
    open my $fh, '<', $name;
    return $fh;
}

sub argv {
    my $self = shift;
    my $name = @{$self->{names}}
             ? $self->{names}->[ $self->{current_file} ]
             : '-'
             ;
    return $name;
}

sub input_line_number {
    my $self = shift;
    my $fh = @{$self->{names}}
           ? $self->{handles}->[$self->{current_file}]
           : \*STDIN
           ;
    return $fh->input_line_number;
}

que pode ser usado como:

package main;

use strict; use warnings;

my $it = My::ARGV::Reader->new(@ARGV);

echo($it);

sub echo {
    my ($it) = @_;
    printf "[%s:%d]:%s", $it, +$it, $_ while <$it>;
}

Output:

[file1:1]:bye bye
[file1:2]:hello
[file1:3]:thank you
[file1:4]:no translation
[file1:5]:
[file2:1]:chao
[file2:2]:hola
[file2:3]:gracias
[file2:4]:

Parece que isso já foi implementado como Iterator::Diamond . Iterator :: Diamante também desabilita a magia 2-argumento-aberto que usa perl ao ler <ARGV>. Melhor ainda, ele suporta a leitura '-' como STDIN, sem habilitar todas as outras magia. Na verdade, eu poderia usá-lo para o efeito apenas em arquivos individuais.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top