Como implementar “geradores” como $ RANDOM?

10

A variável especial $RANDOM tem um novo valor toda vez que é acessada. A este respeito, é uma reminiscência dos objetos "gerador" encontrados em algumas línguas.

Existe uma maneira de implementar algo assim em zsh ?

Eu tentei fazer isso com pipes nomeados, mas não encontrei uma maneira de extrair itens do fifo de maneira controlada sem matar o processo "gerador". Por exemplo:

% mkfifo /tmp/ints
% (index=0
   while ( true )
   do
       echo $index
       index=$(( index + 1 ))
   done) > /tmp/ints &
[1] 16309
% head -1 /tmp/ints
0
[1]  + broken pipe  ( index=0 ; while ( true; ); do; echo $index; index=$(( ...

Existe alguma outra maneira de implementar tal objeto do tipo gerador em zsh?

EDIT: isso não funciona:

#!/usr/bin/env zsh

FIFO=/tmp/fifo-$$
mkfifo $FIFO
INDEX=0
while true; do echo $(( ++INDEX )) > $FIFO; done &
cat $FIFO

Se eu colocar o código acima em um script e executá-lo, a saída raramente será a única linha esperada

1

em vez disso, geralmente consiste em vários inteiros; por exemplo,

1
2
3
4
5

O número de linhas produzidas varia de uma corrida para a próxima.

EDIT2: Como jimmij apontou, a alteração de echo para /bin/echo cuida do problema.

    
por kjo 12.01.2016 / 22:50

3 respostas

10

ksh93 tem disciplinas que são normalmente usadas para esse tipo de coisa. Com zsh , você pode seqüestrar o recurso diretório nomeado dinâmico :

Defina por exemplo:

zsh_directory_name() {
  case $1 in
    (n)
      case $2 in
        (incr) reply=($((++incr)))
      esac
  esac
}

Você pode usar ~[incr] para obter um $incr incrementado a cada vez:

$ echo ~[incr]
1
$ echo ~[incr] ~[incr]
2 3

Sua abordagem falha porque, em head -1 /tmp/ints , o head abre o fifo, lê um buffer completo, imprime uma linha, e depois a fecha . Uma vez fechado, o final da escrita vê um cano quebrado.

Em vez disso, você pode fazer:

$ fifo=~/.generators/incr
$ (umask  077 && mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo)
$ seq infinity > $fifo &
$ exec 3< $fifo
$ IFS= read -rneu3
1
$ IFS= read -rneu3
2

Lá, deixamos o final da leitura aberto no fd 3, e read lê um byte de cada vez, não um buffer completo para ter certeza de ler exatamente uma linha (até o caractere de nova linha).

Ou você poderia fazer:

$ fifo=~/.generators/incr
$ (umask  077 && mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo)
$ while true; do echo $((++incr)) > $fifo; done &
$ cat $fifo
1
$ cat $fifo
2

Naquela época, instanciamos um pipe para cada valor. Isso permite retornar dados contendo qualquer número arbitrário de linhas.

No entanto, nesse caso, assim que cat abrir o fifo, o echo e o loop serão desbloqueados, portanto, mais echo poderá ser executado, quando cat ler o conteúdo e fechar o pipe (fazendo com que o próximo echo instancie um novo canal).

Uma solução alternativa poderia ser adicionar algum atraso, como por exemplo, executando um echo externo como sugerido por @jimmij ou adicionando algum sleep , mas isso ainda não seria muito robusto, ou você poderia recriar o nome canalizar após cada echo :

while 
  mkfifo $fifo &&
  echo $((++incr)) > $fifo &&
  rm -f $fifo
do : nothing
done &

Isso ainda deixa janelas curtas em que o canal não existe (entre unlink() feito por rm e mknod() feito por mkfifo ), fazendo com que cat falhe e janelas muito curtas em que o pipe foi instanciado, mas nenhum processo será gravado novamente nele (entre write() e close() feito por echo ), fazendo com que cat não retorne nada e janelas curtas em que o pipe nomeado ainda existe, mas nada será sempre abri-lo para gravação (entre o close() feito por echo e o unlink() feito por rm ) onde cat irá travar.

Você pode remover algumas dessas janelas fazendo o seguinte:

fifo=~/.generators/incr
(
  umask  077
  mkdir -p $fifo:h && rm -f $fifo && mkfifo $fifo &&
  while
    mkfifo $fifo.new &&
    {
      mv $fifo.new $fifo &&
      echo $((++incr))
    } > $fifo
  do : nothing
  done
) &

Dessa forma, o único problema é se você executar vários cat ao mesmo tempo (todos eles abrem o fifo antes que nosso loop de gravação esteja pronto para abri-lo para gravação). Nesse caso, eles compartilharão a echo output. / p>

Eu também aconselho contra a criação de nomes fixos, fifos legíveis para o mundo (ou qualquer arquivo para esse assunto) em diretórios graváveis mundiais como /tmp a menos que seja um serviço a ser exposto a todos os usuários no sistema.

    
por 12.01.2016 / 23:11
4

Se você quer executar código sempre que o valor de uma variável é lido, você não pode fazer isso dentro do próprio zsh. A variável RANDOM (como outras variáveis especiais semelhantes) é codificada no código-fonte zsh. No entanto, você pode definir variáveis especiais semelhantes escrevendo um módulo em C. Muitos dos módulos padrão define variáveis especiais.

Você pode usar um coprocesso para gerar um gerador.

coproc { i=0; while echo $i; do ((++i)); done }
for ((x=1; x<=3; x++)) { read -p n; echo $n; }

No entanto, isso é bastante limitado porque você só pode ter um coprocessado. Outra maneira de obter a saída de um processo é redirecionar de uma substituição de processo .

exec 3< <(i=0; while echo $i; do ((++i)); done)
for ((x=1; x<=3; x++)) { read n <&3; echo $n; }

Observe que head -1 não funciona aqui, porque lê um buffer inteiro, imprime o que gosta e sai. Os dados que foram lidos do pipe permanecem lidos; essa é uma propriedade intrínseca dos pipes (você não pode inserir dados novamente). O read builtin evita esse problema lendo um byte de cada vez, o que permite que ele pare assim que encontrar a primeira nova linha, mas é muito lento (claro que não importa se você está lendo apenas algumas centenas bytes).

    
por 12.01.2016 / 23:25
1

Acho que faria isso com algum tipo de sinal.

(   trap   "read zero </tmp/ints" PIPE
    while  kill -s PIPE -0
    do     i=$zero
           while echo $((i++))
           do :; done 2>/dev/null >/tmp/ints
    done
)&

Funciona para mim, de qualquer forma.

$ echo  15 >/tmp/ints; head -n 5 </tmp/ints
15
16
17
18
19
$ echo  75 >/tmp/ints; head -n 5 </tmp/ints
75
76
77
78
79

Em uma nota apenas ligeiramente relacionada, aqui está algo estranho que descobri no outro dia:

mkdir nums; cd nums
for n in 0 1 2 3 4 5 6 7
do  ln -s ./ "$n"; done
echo [0-3]/*/*
0/0/0 0/0/1 0/0/2 0/0/3 0/0/4 0/0/5 0/0/6 0/0/7 0/1/0 0/1/1 0/1/2 0/1/3 0/1/4 0/1/5 0/1/6 0/1/7 0/2/0 0/2/1 0/2/2 0/2/3 0/2/4 0/2/5 0/2/6 0/2/7 0/3/0 0/3/1 0/3/2 0/3/3 0/3/4 0/3/5 0/3/6 0/3/7 0/4/0 0/4/1 0/4/2 0/4/3 0/4/4 0/4/5 0/4/6 0/4/7 0/5/0 0/5/1 0/5/2 0/5/3 0/5/4 0/5/5 0/5/6 0/5/7 0/6/0 0/6/1 0/6/2 0/6/3 0/6/4 0/6/5 0/6/6 0/6/7 0/7/0 0/7/1 0/7/2 0/7/3 0/7/4 0/7/5 0/7/6 0/7/7 1/0/0 1/0/1 1/0/2 1/0/3 1/0/4 1/0/5 1/0/6 1/0/7 1/1/0 1/1/1 1/1/2 1/1/3 1/1/4 1/1/5 1/1/6 1/1/7 1/2/0 1/2/1 1/2/2 1/2/3 1/2/4 1/2/5 1/2/6 1/2/7 1/3/0 1/3/1 1/3/2 1/3/3 1/3/4 1/3/5 1/3/6 1/3/7 1/4/0 1/4/1 1/4/2 1/4/3 1/4/4 1/4/5 1/4/6 1/4/7 1/5/0 1/5/1 1/5/2 1/5/3 1/5/4 1/5/5 1/5/6 1/5/7 1/6/0 1/6/1 1/6/2 1/6/3 1/6/4 1/6/5 1/6/6 1/6/7 1/7/0 1/7/1 1/7/2 1/7/3 1/7/4 1/7/5 1/7/6 1/7/7 2/0/0 2/0/1 2/0/2 2/0/3 2/0/4 2/0/5 2/0/6 2/0/7 2/1/0 2/1/1 2/1/2 2/1/3 2/1/4 2/1/5 2/1/6 2/1/7 2/2/0 2/2/1 2/2/2 2/2/3 2/2/4 2/2/5 2/2/6 2/2/7 2/3/0 2/3/1 2/3/2 2/3/3 2/3/4 2/3/5 2/3/6 2/3/7 2/4/0 2/4/1 2/4/2 2/4/3 2/4/4 2/4/5 2/4/6 2/4/7 2/5/0 2/5/1 2/5/2 2/5/3 2/5/4 2/5/5 2/5/6 2/5/7 2/6/0 2/6/1 2/6/2 2/6/3 2/6/4 2/6/5 2/6/6 2/6/7 2/7/0 2/7/1 2/7/2 2/7/3 2/7/4 2/7/5 2/7/6 2/7/7 3/0/0 3/0/1 3/0/2 3/0/3 3/0/4 3/0/5 3/0/6 3/0/7 3/1/0 3/1/1 3/1/2 3/1/3 3/1/4 3/1/5 3/1/6 3/1/7 3/2/0 3/2/1 3/2/2 3/2/3 3/2/4 3/2/5 3/2/6 3/2/7 3/3/0 3/3/1 3/3/2 3/3/3 3/3/4 3/3/5 3/3/6 3/3/7 3/4/0 3/4/1 3/4/2 3/4/3 3/4/4 3/4/5 3/4/6 3/4/7 3/5/0 3/5/1 3/5/2 3/5/3 3/5/4 3/5/5 3/5/6 3/5/7 3/6/0 3/6/1 3/6/2 3/6/3 3/6/4 3/6/5 3/6/6 3/6/7 3/7/0 3/7/1 3/7/2 3/7/3 3/7/4 3/7/5 3/7/6 3/7/7

Também fica mais estranho:

rm *
for a in  a b c d e f g h \
          i j k l m n o p \
          q r s t u v x y z
do 
    ln -s ./ "$a"
done
for a in *
do  echo "$a"/["$a"-z]
done
a/a a/b a/c a/d a/e a/f a/g a/h a/i a/j a/k a/l a/m a/n a/o a/p a/q a/r a/s a/t a/u a/v a/x a/y a/z
b/b b/c b/d b/e b/f b/g b/h b/i b/j b/k b/l b/m b/n b/o b/p b/q b/r b/s b/t b/u b/v b/x b/y b/z
c/c c/d c/e c/f c/g c/h c/i c/j c/k c/l c/m c/n c/o c/p c/q c/r c/s c/t c/u c/v c/x c/y c/z
d/d d/e d/f d/g d/h d/i d/j d/k d/l d/m d/n d/o d/p d/q d/r d/s d/t d/u d/v d/x d/y d/z
e/e e/f e/g e/h e/i e/j e/k e/l e/m e/n e/o e/p e/q e/r e/s e/t e/u e/v e/x e/y e/z
f/f f/g f/h f/i f/j f/k f/l f/m f/n f/o f/p f/q f/r f/s f/t f/u f/v f/x f/y f/z
g/g g/h g/i g/j g/k g/l g/m g/n g/o g/p g/q g/r g/s g/t g/u g/v g/x g/y g/z
h/h h/i h/j h/k h/l h/m h/n h/o h/p h/q h/r h/s h/t h/u h/v h/x h/y h/z
i/i i/j i/k i/l i/m i/n i/o i/p i/q i/r i/s i/t i/u i/v i/x i/y i/z
j/j j/k j/l j/m j/n j/o j/p j/q j/r j/s j/t j/u j/v j/x j/y j/z
k/k k/l k/m k/n k/o k/p k/q k/r k/s k/t k/u k/v k/x k/y k/z
l/l l/m l/n l/o l/p l/q l/r l/s l/t l/u l/v l/x l/y l/z
m/m m/n m/o m/p m/q m/r m/s m/t m/u m/v m/x m/y m/z
n/n n/o n/p n/q n/r n/s n/t n/u n/v n/x n/y n/z
o/o o/p o/q o/r o/s o/t o/u o/v o/x o/y o/z
p/p p/q p/r p/s p/t p/u p/v p/x p/y p/z
q/q q/r q/s q/t q/u q/v q/x q/y q/z
r/r r/s r/t r/u r/v r/x r/y r/z
s/s s/t s/u s/v s/x s/y s/z
t/t t/u t/v t/x t/y t/z
u/u u/v u/x u/y u/z
v/v v/x v/y v/z
x/x x/y x/z
y/y y/z
z/z
    
por 13.01.2016 / 09:30

Tags