$ RANDOM की तरह "जनरेटर" कैसे लागू करें?


10

विशेष चर $RANDOMका हर बार उपयोग किए जाने के बाद नया मान होता है। इस संबंध में, यह कुछ भाषाओं में पाई जाने वाली "जनरेटर" वस्तुओं की याद दिलाता है।

वहाँ कुछ इस तरह से लागू करने के लिए एक रास्ता है zsh?

मैंने नामित पाइपों के साथ ऐसा करने की कोशिश की, लेकिन मुझे "जनरेटर" प्रक्रिया को मारने के बिना नियंत्रित तरीके से फीफो से आइटम निकालने का कोई तरीका नहीं मिला। उदाहरण के लिए:

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

क्या इस तरह के जनरेटर-प्रकार के ऑब्जेक्ट को zsh में लागू करने का कोई और तरीका है?


संपादित करें: यह काम नहीं करता है:

#!/usr/bin/env zsh

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

यदि मैं उपरोक्त को स्क्रिप्ट में रखता हूं, और इसे चलाता हूं, तो आउटपुट शायद ही कभी अपेक्षित सिंगल लाइन हो

1

बल्कि, इसमें आमतौर पर कई पूर्णांक होते हैं; जैसे

1
2
3
4
5

उत्पादित लाइनों की संख्या एक रन से अगले तक भिन्न होती है।

EDIT2: जैसा कि जिमीज ने बताया, समस्या की देखभाल echoकरने के लिए बदल रहा /bin/echoहै।

जवाबों:


10

ksh93इस प्रकार की चीज़ों के लिए आमतौर पर विषयों का उपयोग किया जाता है। इसके साथ zsh, आप डायनामिक डाइरेक्ट्री फ़ीचर को हाईजैक कर सकते हैं :

उदाहरण के लिए परिभाषित करें:

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

और फिर आप हर बार ~[incr]एक वेतन वृद्धि प्राप्त करने के लिए उपयोग कर सकते हैं $incr:

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

आपका दृष्टिकोण विफल हो जाता है क्योंकि head -1 /tmp/ints, सिर पंद्रह को खोलता है, एक पूर्ण बफर पढ़ता है, एक पंक्ति को प्रिंट करता है, और फिर इसे बंद कर देता है । एक बार बंद हो जाने पर, लेखन छोर टूटे हुए पाइप को देखता है।

इसके बजाय, आप या तो कर सकते हैं:

$ 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

वहां, हम fd 3 पर रीडिंग एंड को खुला छोड़ देते हैं, और readएक बार में एक बाइट को पढ़ते हैं, न कि एक पूरी बफ़र को बिल्कुल एक लाइन (न्यूलाइन कैरेक्टर तक) पढ़ना सुनिश्चित करते हैं।

या आप कर सकते हैं:

$ 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

उस समय, हम हर मूल्य के लिए एक पाइप इंस्टेंट करते हैं। यह किसी भी मनमाने ढंग से लाइनों वाली डेटा को वापस करने की अनुमति देता है।

हालाँकि, उस स्थिति में, जैसे ही catपंद्रो खुलता है, echoऔर लूप अनब्लॉक हो जाता है, इसलिए अधिक echoचलाया जा सकता है, catतब तक सामग्री को पढ़ता है और पाइप को बंद कर देता है (अगले echoएक नए पाइप को इंस्टेंट करने का कारण बनता है )।

आस-पास का काम कुछ विलंब को जोड़ने के लिए हो सकता है, उदाहरण के लिए echo@jimmij द्वारा सुझाए गए अनुसार एक बाहरी रनिंग या कुछ जोड़ना sleep, लेकिन यह अभी भी बहुत मजबूत नहीं होगा, या आप प्रत्येक के बाद नामित पाइप को फिर से बना सकते हैं echo:

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

यही कारण है कि अभी भी कम खिड़कियों जहां पाइप मौजूद नहीं है (के बीच छोड़ देता है unlink()के द्वारा किया जाता rmहै और mknod()के द्वारा किया mkfifo) के कारण catविफल, और बहुत ही कम खिड़कियों जहां पाइप instantiated कर दिया गया है, लेकिन कोई प्रक्रिया कभी यह करने के लिए फिर से लिखेंगे (के बीच write()और close()द्वारा किया गया echo) catकुछ भी नहीं लौटाने के कारण , और छोटी खिड़कियां जहां नामित पाइप अभी भी मौजूद है, लेकिन कुछ भी इसे लिखने के लिए कभी नहीं खोलेगा ( close()द्वारा किया गया echoऔर उसके unlink()द्वारा किया गया rm) जहां catलटका होगा।

आप उनमे से कुछ विंडो को हटा सकते है जैसे:

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

इस तरह, केवल समस्या यह है कि यदि आप एक ही समय में कई बिल्ली चलाते हैं (वे सभी हमारे लेखन लूप से पहले इसे खोलने के लिए तैयार होने से पहले पंद्रो खोलते हैं) तो उस स्थिति में वे echoआउटपुट साझा करेंगे ।

/tmpजब तक यह सिस्टम पर सभी उपयोगकर्ताओं के सामने आने वाली सेवा नहीं है , तब तक मैं विश्व के योग्य निर्देशिकाओं में निश्चित नाम, विश्व पठनीय पंद्रह (या उस मामलों के लिए कोई भी फ़ाइल) बनाने के खिलाफ सलाह दूंगा ।


धन्यवाद। जब तक मैंने कोई गलती नहीं की है, आपके द्वारा दिया गया आखिरी नुस्खा हमेशा काम नहीं करता है। मेरा EDIT देखिए।
कोजो

1
@kjo कोशिश करो command echoया /bin/echoनिर्मित के बजाय echo। इसके अलावा - आप इस कमांड को थोड़ा छोटा कर सकते हैं repeat 999 /bin/echo $((++incr)) > /tmp/int &:।
जिमीज

1
@kjo, संपादित देखें।
स्टीफन चेजलस

4

यदि आप किसी वैरिएबल का मान जब भी कोड निष्पादित करना चाहते हैं, तो आप zsh के अंदर ऐसा नहीं कर सकते। RANDOMचर (अन्य इसी तरह के विशेष चर) की तरह हार्ड-कोडेड zsh स्रोत कोड में है। आप हालांकि सी में एक मॉड्यूल लिखकर इसी तरह के विशेष चर को परिभाषित कर सकते हैं। कई मानक मॉड्यूल विशेष चर को परिभाषित करते हैं।

आप एक जनरेटर बनाने के लिए एक कोप्रोसेस का उपयोग कर सकते हैं ।

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

हालाँकि यह बहुत सीमित है क्योंकि आपके पास केवल एक कोप्रोसेस हो सकता है। एक प्रक्रिया से उत्तरोत्तर उत्पादन प्राप्त करने का दूसरा तरीका एक प्रक्रिया प्रतिस्थापन से पुनर्निर्देशित करना है ।

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

ध्यान दें कि head -1यहां काम नहीं करता है, क्योंकि यह एक पूरे बफर को पढ़ता है, जो पसंद करता है उसे प्रिंट करता है और बाहर निकालता है। पाइप से पढ़ा जाने वाला डेटा रीड रहता है; यह पाइपों की आंतरिक संपत्ति है (आप डेटा वापस नहीं भर सकते हैं)। readनिर्मित एक समय है, जो यह जैसे ही यह पहले न्यू लाइन पाता रोकने के लिए अनुमति देता है, लेकिन बहुत धीमी है पर एक बाइट को पढ़ कर इस मुद्दे से बचा जाता है (बेशक उस बात करता है, तो आप सिर्फ कुछ सौ बाइट्स पढ़ रहे हैं नहीं है की)।


2
Zsh में एक समय में केवल एक ही पुलिस है? मैं आश्चर्यचकित हूं - यह अक्सर नहीं होता है मैं एक ऐसी जगह देखता हूं जहां बैश अधिक लचीला होता है। :)
चार्ल्स डफी

@CharlesDuffy, आपके पास zsh में एक से अधिक कॉपीरोसेस हो सकते हैं । कोप्रोसेस केवल हाल ही में जोड़े गए हैं bash, उस लिंक पर बैश अनुभाग देखें।
स्टीफन चेजलस

@ स्टेफेनचैलेज आप zsh में एक से अधिक कॉपीरोग के साथ कैसे बातचीत करते हैं? ( coprocकॉप्रेसिस, मेरा मतलब है, खाली नहीं)
गिल्स

उसी तरह ksh के साथ जैसा कि उस लिंक पर बताया गया है। coproc cmd1; exec 3>&p 4<&p; coproc cmd2 3>&- 4<&-...
स्टीफन चेजलस

1

मुझे लगता है कि मैं इसे किसी तरह के संकेत के साथ करूंगा।

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

यह मेरे लिए वैसे भी काम करता है।


$ 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

केवल थोड़े से संबंधित नोट पर, यहाँ कुछ अजीब है जो मैंने दूसरे दिन खोजा:

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

यह भी अजीब हो जाता है:

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

इसमें अजीब क्या है?
स्टीफन चेज़लस

@ स्टीफनचेज़लस - यह सिर्फ अजीब लग रहा था कि लिंक आत्म-पुनरावृत्ति करेंगे। और इतनी आसानी से। मुझे लगा कि यह अजीब था। और शांत। मैंने यह भी सोचा था कि किसी प्रकार की गहराई पुनरावृत्ति की सीमा होनी चाहिए - लगता है कि शेल ट्रिगर होगा - या क्या वास्तव में एक ही पथ में 40 लिंक करने की आवश्यकता है?
mikeserv


@ स्टीफनचेज़ेलस - यह अच्छा है। लेकिन शायद bashव्यवहार बदल गया है? मुझे लगता है कि pwdकेवल जाँच और संदर्भ नहीं देने का कथन $PWDगलत है। mkdir /tmp/dir; cd $_; PS4='$OLDPWD, $PWD + '; set -x; OLDPWD=$OLDPWD PWD=$PWD command eval ' cd ..; cd ..; cd ~; pwd'; pwd; cd .; pwdआपको दिखा सकता हूं कि मेरा क्या मतलब है। यह एक समस्या है जिसने मुझे w / इस ns()चीज़ को खराब कर दिया है।
15
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.