Làm thế nào để triển khai các máy phát điện của Nhật Bản như $ RANDOM?


10

Biến đặc biệt $RANDOMcó giá trị mới mỗi lần truy cập. Về mặt này, nó gợi nhớ đến các đối tượng "trình tạo" được tìm thấy trong một số ngôn ngữ.

Có cách nào để thực hiện một cái gì đó như thế này trong zsh?

Tôi đã cố gắng làm điều này với các đường ống được đặt tên, nhưng tôi không tìm được cách trích xuất các vật phẩm từ fifo một cách có kiểm soát mà không làm hỏng quá trình "máy phát điện". Ví dụ:

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

Có cách nào khác để thực hiện một đối tượng kiểu trình tạo như vậy trong zsh không?


EDIT: Điều này không hoạt động:

#!/usr/bin/env zsh

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

Nếu tôi đặt ở trên trong một tập lệnh và chạy nó, đầu ra hiếm khi là dòng đơn dự kiến

1

đúng hơn, nó thường bao gồm một số nguyên; ví dụ

1
2
3
4
5

Số lượng dòng sản xuất thay đổi từ một chạy đến tiếp theo.

EDIT2: Như jimmij đã chỉ ra, thay đổi echođể /bin/echogiải quyết vấn đề.

Câu trả lời:


10

ksh93có các quy tắc thường được sử dụng cho loại điều này. Với zsh, bạn có thể chiếm quyền điều khiển các thư mục động đặt tên tính năng :

Xác định ví dụ:

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

Và sau đó bạn có thể sử dụng ~[incr]để tăng dần $incrmỗi lần:

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

Cách tiếp cận của bạn không thành công bởi vì head -1 /tmp/ints, đầu mở fifo, đọc một bộ đệm đầy đủ, in một dòng và sau đó đóng nó . Sau khi đóng lại, kết thúc bằng văn bản nhìn thấy một đường ống bị hỏng.

Thay vào đó, bạn có thể làm:

$ 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

Ở đó, chúng tôi để kết thúc đọc mở trên fd 3 và readđọc một byte mỗi lần, không phải là bộ đệm đầy đủ để đảm bảo đọc chính xác một dòng (tối đa ký tự dòng mới).

Hoặc bạn có thể làm:

$ 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

Thời gian đó, chúng tôi khởi tạo một đường ống cho mọi giá trị. Điều đó cho phép trả về dữ liệu chứa bất kỳ số lượng dòng tùy ý.

Tuy nhiên, trong trường hợp đó, ngay khi catmở fifo, echovòng lặp và vòng lặp được bỏ chặn, do đó echocó thể chạy nhiều hơn , đến lúc catđọc nội dung và đóng đường ống (khiến đường tiếp theo echokhởi tạo một đường ống mới).

Một công việc xung quanh có thể là thêm một số độ trễ, ví dụ như bằng cách chạy một bên ngoài echonhư được đề xuất bởi @jimmij hoặc thêm một số sleep, nhưng điều đó vẫn sẽ không mạnh mẽ, hoặc bạn có thể tạo lại đường ống được đặt tên sau mỗi lần echo:

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

Điều đó vẫn để lại các cửa sổ ngắn nơi đường ống không tồn tại (giữa unlink()được thực hiện bởi rmmknod()được thực hiện bởi mkfifo) gây ra catlỗi và các cửa sổ rất ngắn nơi đường ống đã được khởi tạo nhưng không có quá trình nào sẽ ghi lại vào đó (giữa write()close()được thực hiện bởi echo) gây ra catkhông trả lại gì, và các cửa sổ ngắn nơi đường ống được đặt tên vẫn tồn tại nhưng không có gì sẽ mở nó để viết (giữa close()được thực hiện bởi echounlink()được thực hiện bởi rm) nơi catsẽ treo.

Bạn có thể loại bỏ một số cửa sổ bằng cách thực hiện như sau:

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

Theo cách đó, vấn đề duy nhất là nếu bạn chạy nhiều con mèo cùng một lúc (tất cả chúng đều mở fifo trước khi vòng lặp viết của chúng tôi sẵn sàng mở nó để viết) trong trường hợp chúng sẽ chia sẻ echođầu ra.

Tôi cũng khuyên bạn không nên tạo tên cố định, fifos có thể đọc được trên thế giới (hoặc bất kỳ tệp nào cho vấn đề đó) trong các thư mục có thể ghi trên thế giới /tmptrừ khi đó là dịch vụ được hiển thị cho tất cả người dùng trên hệ thống.


Cảm ơn. Trừ khi tôi mắc lỗi, công thức cuối cùng bạn đưa ra không phải lúc nào cũng hiệu quả. Xem EDIT của tôi.
kjo

1
@kjo Hãy thử command echohoặc /bin/echothay vì tích hợp echo. Ngoài ra - bạn có thể thực hiện lệnh này ngắn hơn một chút : repeat 999 /bin/echo $((++incr)) > /tmp/int &.
jimmij

1
@kjo, xem chỉnh sửa.
Stéphane Chazelas

4

Nếu bạn muốn thực thi mã bất cứ khi nào giá trị của biến được đọc, bạn không thể thực hiện điều đó trong chính zsh. Các RANDOMbiến (như các biến đặc biệt tương tự khác) được mã hóa cứng trong mã nguồn zsh. Tuy nhiên, bạn có thể xác định các biến đặc biệt tương tự bằng cách viết một mô-đun bằng C. Nhiều mô-đun chuẩn xác định các biến đặc biệt.

Bạn có thể sử dụng một bộ đồng xử lý để tạo ra một máy phát điện.

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

Tuy nhiên điều này khá hạn chế vì bạn chỉ có thể có một bộ đồng xử lý. Một cách khác để tăng dần đầu ra từ một quy trình là chuyển hướng từ thay thế quy trình .

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

Lưu ý rằng head -1nó không hoạt động ở đây, bởi vì nó đọc toàn bộ bộ đệm, in ra những gì nó thích và thoát. Dữ liệu được đọc từ đường ống vẫn được đọc; đây là một thuộc tính nội tại của đường ống (bạn không thể đưa dữ liệu trở lại). Nội dung readnày tránh được vấn đề này bằng cách đọc từng byte một, điều này cho phép nó dừng ngay khi tìm thấy dòng mới đầu tiên nhưng rất chậm (tất nhiên điều đó không quan trọng nếu bạn chỉ đọc vài trăm byte).


2
Chỉ có một bộ đồng xử lý tại một thời điểm trong zsh? Tôi ngạc nhiên - không thường xuyên tôi thấy một nơi mà bash linh hoạt hơn. :)
Charles Duffy

@CharlesDuffy, bạn có thể có nhiều hơn một bộ đồng xử lý trong zsh . coprocesses chỉ mới được thêm vào gần đây bash, xem phần bash tại liên kết đó.
Stéphane Chazelas

@ StéphaneChazelas Làm thế nào để bạn tương tác với nhiều hơn một đồng xử lý trong zsh? ( coprocCoprocesses, ý tôi là, những người không zpty)
Gilles 'Somali dừng vốn là xấu'

Cách tương tự như với ksh như được giải thích tại liên kết đó. coproc cmd1; exec 3>&p 4<&p; coproc cmd2 3>&- 4<&-...
Stéphane Chazelas

1

Tôi nghĩ rằng tôi sẽ làm điều đó với một tín hiệu của một số loại.

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

Nó làm việc cho tôi, dù sao.


$ 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

Trên một ghi chú chỉ liên quan một chút, đây là một điều kỳ lạ tôi đã phát hiện ra vào một ngày khác:

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

Nó cũng lạ hơn:

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

Có gì lạ trong đó?
Stéphane Chazelas

@ StéphaneChazelas - có vẻ kỳ lạ là các liên kết sẽ tự tái diễn. Và thật dễ dàng. Tôi nghĩ nó thật kỳ lạ. Và mát mẻ. Tôi cũng nghĩ rằng đã có một loại giới hạn đệ quy độ sâu nào đó - dường như lớp vỏ sẽ kích hoạt điều đó - hoặc nó thực sự cần phải thực hiện 40 liên kết trong một đường dẫn?
mikeerv


@ StéphaneChazelas - Điều đó tốt. Nhưng có lẽ bashhành vi của họ đã thay đổi? Tôi nghĩ rằng tuyên bố về việc pwdkhông kiểm tra và chỉ tham khảo $PWDlà không chính xác. mkdir /tmp/dir; cd $_; PS4='$OLDPWD, $PWD + '; set -x; OLDPWD=$OLDPWD PWD=$PWD command eval ' cd ..; cd ..; cd ~; pwd'; pwd; cd .; pwdcó thể cho bạn thấy những gì tôi có ý nghĩa. Đó là một vấn đề khiến tôi đau đầu với điều này ns().
mikeerv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.