Como fazer com que dois comandos consumam a entrada de stdin sequencialmente?

3

O seguinte script perl consume-10-lines-and-exit lê dez linhas de stdin e as imprime. Depois disso, sai.

#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';

for (my $i = 0; $i < 10; $i++) {
    my $line = <STDIN>;
    print $line;
}

Estou tentando combinar consume-10-lines-and-exit e cat de tal forma que as dez primeiras linhas de entrada e consumidas e impressas pelo primeiro comando e depois o restante sejam consumidas por cat .

O código a seguir, alguns pequenos trechos, imprimem de 1 a 10, em vez de 1 a 13, como eu esperava.

printf '1 2 3 4 5 6 7 8 9 10 11 12 13' \
    | tr ' ' '\n' | { perl consume-10-lines-and-exit; cat; }

printf '1 2 3 4 5 6 7 8 9 10 11 12 13' \
    | tr ' ' '\n' | ( perl consume-10-lines-and-exit; cat )

printf '1 2 3 4 5 6 7 8 9 10 11 12 13' \
    | tr ' ' '\n' | sh -c 'perl consume-10-lines-and-exit; cat'

Existe uma construção para comandos de sequenciamento para que eles leiam a entrada de stdin até que eles saiam e então o próximo comando continuará onde o anterior parou?

    
por Gregory Nisbet 08.04.2016 / 18:27

3 respostas

3

Seu problema é que o perl está usando a entrada em buffer, portanto, lê com antecedência além das linhas que você deseja consumir. Experimente esta versão de byte a byte:

perl -e '
use strict;
use warnings FATAL => "all";
for (my $i = 0; $i < 10; $i++) {
  my $line;
  while(sysread(*STDIN,my $char,1)==1){
      $line .= $char;
      last if $char eq "\n";
  }
  print $line;
}'
    
por 08.04.2016 / 18:50
3

Usar os comandos de agrupamento é certo para fazer esse trabalho, mas o culpado é a entrada que você coloca nos comandos de agrupamento, o que não é possível.

O POSIX define na seção INPUT FILE que:

When a standard utility reads a seekable input file and terminates without an error before it reaches end-of-file, the utility shall ensure that the file offset in the open file description is properly positioned just past the last byte processed by the utility. For files that are not seekable, the state of the file offset in the open file description for that file is unspecified

perl não é um utilitário padrão, mas há uma grande chance de seguir as especificações padrão. Se você tornar seu arquivo pesquisável, ele funcionará:

$ seq 13 > /tmp/test
$ {perl -e 'for($i=0;$i<10;$i++){$line = <STDIN>;print $line}'; cat;} </tmp/test
1
2
3
4
5
6
7
8
9
10
11
12
13

Se você não quiser usar o arquivo pesquisável, poderá forçar perl a usar a camada inferior que a função de chamadas do sistema de chamadas read() , write() , lseek() para IO:

$ seq 13 | {
  PERLIO=:unix perl -e 'for($i=0;$i<10;$i++){$line = <STDIN>;print $line}'
  cat
}
    
por 08.04.2016 / 19:10
1

O armazenamento em buffer também pode ser desativado por meio do uso de camadas PerlIO (consulte também perlrun ). Além disso, esse loop desajeitado e detalhado no estilo C pode ser mais facilmente escrito como ... for 1..10 ou for my $i (1..10) { ... } .

$ perl -E 'say for 1..12' \
| ( PERLIO=:unix perl -e 'print "perl " . scalar <STDIN> for 1..10'; cat )
perl 1
perl 2
perl 3
perl 4
perl 5
perl 6
perl 7
perl 8
perl 9
perl 10
11
12
$ 
    
por 08.04.2016 / 19:05

Tags