Como posso redefinir um identificador de arquivo recebendo entrada padrão no Perl?
Pergunta
Quando emite um script perl alguma entrada padrão, por exemplo
$ awk '{print $1"\t"$2}' foo.txt | myScript.pl
Eu tenho um script que contém um bug. Ele lê a primeira linha de entrada padrão, obtém algo dessa primeira linha e, em seguida, apenas as linhas 2 a n da entrada padrão na leitura subsequente:
open (FH, "< $input") or die $?;
my $firstLine = <FH>; // reads first line
...
while (my $line = <FH>) {
// reads lines 2 through n
}
close (FH);
Então eu adicionei um seek
Declaração para este script para tentar redefinir o identificador de arquivo para o início do arquivo:
use Fcntl qw(:seek);
...
open (FH, "< $input") or die $?;
my $firstLine = <FH>; // reads first line
seek (FH, 0, SEEK_SET) or die "error: could not reset file handle\n"; // should reset file handle
...
while (my $line = <FH>) {
// reads lines 1 through n (in theory)
}
close (FH);
Adicionando isso seek
A instrução funciona para entrada de arquivo, mas não para entrada padrão. O script trava com o erro:
error: could not reset file handle
Como posso ler corretamente a primeira linha de entrada padrão no início do script, redefinir a alça do arquivo e depois ler todas as linhas 1 a n da entrada padrão?
Acho que posso escrever um caso especial para armazenar e processar a primeira linha antes do while
Loop, mas espero que haja uma solução mais limpa para esse problema que me permite lidar com a entrada padrão e a entrada do arquivo.
Solução
Existe uma maneira fácil:
open (FH, "< $input") or die $?;
my $line = <FH>; // reads first line
//do stuff with first line
do {
//stuff
} while ($line = <FH>);
Outras dicas
EDITAR: Desvantagem da minha primeira abordagem é que ela deve carregar todas as linhas de FH
na memória antes de processar a lista. Essa abordagem, usando o recurso de referência aberto ao escalar (disponível desde 5.8.0) não terá esse problema:
my $firstline = <FH>;
open(my $f1, '<', \$firstline);
...
while (my $line = <$f1> || <FH>) {
# process line 1 through n
}
Que tal:
for my $line ($firstline, <FH>) {
# process lines 1 through n
}
Eu não acho que você possa redefinir (ou procurar (0)) para stdin. Não é um identificador de arquivo normal como stdin. Como não é realmente um arquivo, você exigiria o buffer resetável do STDIN.
Eu acho que você precisará lidar com a leitura e a reutilização da linha 1, especialmente.