$ RANDOM gibi “jeneratörler” nasıl uygulanır?


10

Özel değişken $RANDOMher erişildiğinde yeni bir değere sahiptir. Bu bakımdan, bazı dillerde bulunan "jeneratör" nesnelerini anımsatmaktadır.

Böyle bir şeyi uygulamanın bir yolu var mı zsh?

Ben adlandırılmış borular ile bunu yapmaya çalıştım, ama "jeneratör" sürecini öldürmeden kontrollü bir şekilde fifo öğeleri çıkarmak için bir yol bulamadım. Örneğin:

% 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=$(( ...

Böyle bir jeneratör tipi nesneyi zsh'de uygulamanın başka bir yolu var mı?


EDIT: Bu çalışmıyor:

#!/usr/bin/env zsh

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

Yukarıdakileri bir betiğe koyar ve çalıştırırsam, çıktı nadiren beklenen tek satır

1

bunun yerine, genellikle birkaç tamsayıdan oluşur; Örneğin

1
2
3
4
5

Üretilen hat sayısı bir çalışmadan diğerine değişir.

EDIT2: jimmij belirttiği gibi, değişen echoiçin /bin/echosorun ilgilenir.

Yanıtlar:


10

ksh93tipik olarak bu tür şeyler için kullanılan disiplinlere sahiptir. İle zsh, dinamik adlı dizin özelliğini ele geçirebilirsiniz :

Örneğin tanımlayın:

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

Ve sonra her seferinde ~[incr]bir artış elde etmek için kullanabilirsiniz $incr:

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

Yaklaşımınız başarısız olur, çünkü head -1 /tmp/intskafa, fifoyu açar, tam bir arabelleği okur, bir satır yazdırır ve sonra kapatır . Kapandıktan sonra yazı ucu kırık bir boru görür.

Bunun yerine, şunları yapabilirsiniz:

$ 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

Orada, okuma ucunu fd 3'te açık bırakıyoruz ve readtam olarak bir satırı (yeni satır karakterine kadar) okuduğunuzdan emin olmak için tam bir arabellek değil, bir seferde bir bayt okuyoruz.

Veya şunları yapabilirsiniz:

$ 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

O zaman, her değer için bir boru başlatırız. Bu, herhangi bir rasgele sayıda satır içeren verilerin döndürülmesine izin verir.

Bununla birlikte, bu durumda, catfifo açılır açılmaz echove döngü engellenmez, böylece içeriği okuyarak ve boruyu kapatıncaya kadar daha fazlası echoçalıştırılabilir cat(bir sonrakinin echoyeni bir boru başlatmasına neden olur ).

Bir çözüm, örneğin echo@jimmij tarafından önerildiği gibi bir harici çalıştırma veya biraz ekleme gibi bir gecikme eklemek olabilir sleep, ancak yine de çok sağlam olmaz veya her birinden sonra adlandırılmış boruyu yeniden oluşturabilirsiniz echo:

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

Hala (arasındaki boru yok kısa pencereleri bırakır unlink()yapılır rmve mknod()yapılır mkfifoneden olan) catbaşarısız olmasına ve aralarında (boru örneği olmuştur ama hiçbir işlem hiç ona tekrar yazacak çok kısa pencereler write()ve close()tarafından yapılan echoyol açan) catadlandırılmış kanal yayınlanmaya devam ederken arasındaki hiçbir şey (yazma için açacağız nereye hiçbir şey dönmek ve kısa pencereler close()yapılır echove unlink()yapılır rmnerede) cataskıda.

Bu pencerelerden bazılarını şöyle yaparak kaldırabilirsiniz :

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
) &

Bu şekilde, tek sorun aynı anda birden fazla kedi çalıştırırsanız (hepsi yazma döngüsünü yazmak için açmaya hazır olmadan önce fifo'yu açarlar), bu durumda echoçıktıyı paylaşacaklardır .

Ayrıca /tmp, sistemdeki tüm kullanıcılara açık bir hizmet olmadıkça , dünya çapında yazılabilir dizinlerde sabit ad, dünya tarafından okunabilir fifos (veya bu konuda herhangi bir dosya) oluşturmaya karşı öneriyorum .


Teşekkürler. Bir hata yapmadıkça, verdiğiniz son tarif her zaman işe yaramaz. EDIT'ime bakın.
kjo

1
@kjo deneyin command echoveya /bin/echoyerleşik yerine echo. Ayrıca - Biraz daha kısa biraz bu komutu yapabilirsiniz: repeat 999 /bin/echo $((++incr)) > /tmp/int &.
jimmij

1
@kjo, düzenlemeye bakın.
Stéphane Chazelas

4

Bir değişkenin değeri her okunduğunda kod yürütmek istiyorsanız, bunu zsh içinde yapamazsınız. RANDOM(Diğer benzeri özel değişkenler gibi) değişken kodlanmış Zsh kaynak kodu bulunmaktadır. Bununla birlikte, C'ye bir modül yazarak benzer özel değişkenleri tanımlayabilirsiniz. Standart modüllerin çoğu özel değişkenleri tanımlar.

Jeneratör yapmak için bir yardımcı işlem kullanabilirsiniz .

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

Ancak bu oldukça sınırlıdır, çünkü yalnızca bir işleminiz olabilir. Bir işlemden aşamalı olarak çıktı almanın başka bir yolu, işlem ikamesinden yeniden yönlendirmektir .

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

Not head -1o, bütün bir tampon okur bunu sever ne yazdırır ve çıkışlar nedeniyle, burada çalışmıyor. Borudan okunan veriler okunmaya devam eder; Bu, boruların gerçek bir özelliğidir (verileri geri dolduramazsınız). readBuiltin ilk satırsonu bulduğu en kısa sürede durdurmak için izin verir ama çok yavaş bir dönemde, bir bayt okuyarak bu sorunu önler (sadece birkaç yüz bayt okuyorsanız önemli değil tabii ki).


2
Zsh'de bir seferde sadece tek bir işlem var mı? Şaşırdım - genellikle bash'ın daha esnek olduğu bir yer görmüyorum. :)
Charles Duffy

@CharlesDuffy, zsh içinde birden fazla işleminiz olabilir . yardımcı işlemler yalnızca son zamanlarda eklendi bash, bu bağlantıdaki bash bölümüne bakın.
Stéphane Chazelas

@ StéphaneChazelas zsh'de birden fazla işlem ile nasıl etkileşime giriyorsunuz? ( coprocyardımcı işlemler, yani, zpty olanlar değil)
Gilles 'SO- kötü olmayı bırak'

Bu bağlantıda açıklandığı gibi ksh ile aynı şekilde. coproc cmd1; exec 3>&p 4<&p; coproc cmd2 3>&- 4<&-...
Stéphane Chazelas

1

Sanırım bunu bir tür sinyalle yapacağım.

(   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
)&

Her neyse benim için çalışıyor.


$ 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

Sadece biraz ilgili bir notta, işte geçen gün keşfettiğim garip bir şey:

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

Garipleşir:

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

Ne var tuhaf İçinde?
Stéphane Chazelas

@ StéphaneChazelas - bağlantıların kendi kendine gelmesi çok tuhaf görünüyordu. Ve çok kolay. Garip olduğunu düşündüm. Ve soğuk. Ayrıca bir tür derinlik özyineleme sınırı olması gerektiğini düşündüm - kabuk bunu tetikleyecek gibi görünüyor - ya da aslında tek bir yolda 40 bağlantı yapması gerekiyor mu?
mikeserv


@ StéphaneChazelas - Güzel. Ama belki de bashdavranışı değişti? Bence pwdsadece kontrol etmeme ve göndermemeyle ilgili ifade $PWDyanlış mkdir /tmp/dir; cd $_; PS4='$OLDPWD, $PWD + '; set -x; OLDPWD=$OLDPWD PWD=$PWD command eval ' cd ..; cd ..; cd ~; pwd'; pwd; cd .; pwdne demek istediğimi gösterebilir. Bu ns()şey beni rahatsız eden bir problem .
mikeserv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.