$ RANDOM과 같은 "제너레이터"를 구현하는 방법은 무엇입니까?


10

특수 변수 $RANDOM는 액세스 할 때마다 새로운 값을 갖습니다. 이와 관련하여 일부 언어에서는 "생성기"개체를 연상시킵니다.

이와 같은 것을 구현하는 방법이 zsh있습니까?

명명 된 파이프를 사용하여이 작업을 시도했지만 "제너레이터"프로세스를 종료하지 않고 제어 된 방식으로 fifo에서 항목을 추출하는 방법을 찾지 못했습니다. 예를 들면 다음과 같습니다.

% 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 : jimmij가 지적했듯이 문제를 처리 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/intshead가 fifo를 열고 전체 버퍼를 읽고 한 줄을 인쇄 한 다음 닫기 때문에 접근 방식이 실패 합니다 . 일단 닫으면 쓰기 끝에서 파이프가 파손 된 것을 볼 수 있습니다.

대신 다음 중 하나를 수행 할 수 있습니다.

$ 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

그때 우리는 모든 가치에 대해 파이프를 인스턴스화합니다. 임의의 수의 행을 포함하는 데이터를 리턴 할 수 있습니다.

그러나이 경우 catfifo 를 열 자마자 echoand 및 루프가 차단되지 않으므로 내용을 읽고 파이프를 닫을 echo때까지 더 많은 것을 실행할 수 있습니다 cat(다음 echo은 새 파이프를 인스턴스화합니다).

해결 방법은 echo@jimmij가 제안한 것처럼 외부 를 실행하거나 일부를 추가하는 것과 같이 약간의 지연을 추가 sleep하는 것이 될 수 있지만 여전히 강력하지는 않지만 각각 후에 명명 된 파이프를 다시 만들 수 있습니다 echo.

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

즉 여전히 (사이에 파이프가 존재하지 않는 짧은 창 잎 unlink()수행 rmmknod()수행 mkfifo의 원인이) cat실패하고 사이 (파이프가 인스턴스화되고 있지만 프로세스가 이제까지 그것을 다시 쓸 것이다 매우 짧은 창문 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
) &

그런 식으로, 유일한 문제는 동시에 여러 고양이를 실행하는 경우 (쓰기 루프가 쓰기 위해 열 준비가되기 전에 모두 fifo를 여는 경우) echo출력 을 공유하는 것 입니다.

또한 /tmp시스템의 모든 사용자에게 노출되는 서비스가 아닌 한 쓰기 가능한 디렉토리에 고정 된 이름, 읽을 수있는 fifo (또는 해당 파일)를 작성 하지 않는 것이 좋습니다.


감사. 내가 실수하지 않은 한, 당신이주는 마지막 조리법이 항상 효과가있는 것은 아닙니다. 내 편집을 참조하십시오.
kjo

1
@kjo 내장 command echo또는 /bin/echo대신 사용해보십시오 echo. 또한-이 명령을 조금 더 짧게 만들 수 있습니다 repeat 999 /bin/echo $((++incr)) > /tmp/int &.
jimmij

1
@kjo, 편집 참조.
Stéphane Chazelas

4

변수의 값을 읽을 때마다 코드를 실행하려면 zsh 자체에서 코드를 실행할 수 없습니다. RANDOM(다른 유사한 특별한 변수 등) 가변 하드 코딩 zsh을 소스 코드이다. 그러나 C로 모듈을 작성하여 유사한 특수 변수를 정의 할 수 있습니다. 많은 표준 모듈이 특수 변수를 정의합니다.

코 프로세스 를 사용하여 생성기를 만들 수 있습니다 .

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에는 한 번에 하나의 코 프로세스 만 있습니까? 나는 놀랐다-bash가 더 유연한 곳을 종종 보지 못한다. :)
Charles Duffy

@CharlesDuffy, zsh에서 둘 이상의 coprocess를 가질 수 있습니다 . coprocesses는 최근에에 추가되었으므로 bash해당 링크의 bash 섹션을 참조하십시오.
Stéphane Chazelas

@ StéphaneChazelas zsh에서 하나 이상의 공동 프로세스와 어떻게 상호 작용합니까? ( coproc공식 프로세스, 나는 zpty가 아니라는 것을 의미한다)
Gilles 'SO- 악의 존재 중지'

해당 링크에 설명 된 ksh와 동일한 방식입니다. coproc cmd1; exec 3>&p 4<&p; coproc cmd2 3>&- 4<&-...
Stéphane Chazelas

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

뭐가 이상해 ?
Stéphane Chazelas

@ StéphaneChazelas-링크가 자체 재귀하는 것이 이상하게 보였습니다. 그리고 너무 쉽게. 나는 그것이 이상하다고 생각했다. 그리고 시원하다. 또한 일종의 깊이 재귀 제한이 있어야한다고 생각했습니다. 쉘이 트리거 할 것 같습니다. 또는 실제로 단일 경로에서 40 개의 링크를 수행해야합니까?
mikeserv


@ StéphaneChazelas-좋습니다. 하지만 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내가 무슨 뜻인지 보여줄 수 있습니다. 이 문제로 나를 괴롭힌 문제입니다 ns().
mikeserv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.