상호 연결된 명령간에 순환 데이터 흐름을 어떻게 구현할 수 있습니까?


19

명령을 서로 연결할 수있는 두 가지 유형을 알고 있습니다.

  1. 파이프를 사용하여 (다음 명령의 표준 입력에 표준 출력을 입력).
  2. Tee를 사용하여 (출력을 여러 출력에 결합).

이것이 가능한지 모르겠으므로 가상 연결 유형을 그립니다.

여기에 이미지 설명을 입력하십시오

예를 들어이 의사 코드와 같이 명령 대신 변수를 사용하는 명령 사이에서 순환 데이터 흐름을 구현하는 방법은 무엇입니까?

pseudo-code:

a = 1    # start condition 

repeat 
{
b = tripple(a)
c = sin(b) 
a = c + 1 
}

답변:


16

로 구현되는 원형 I / O 루프 tail -f

이것은 순환 I / O 루프를 구현합니다.

$ echo 1 >file
$ tail -f file | while read n; do echo $((n+1)); sleep 1; done | tee -a file
2
3
4
5
6
7
[..snip...]

이것은 당신이 언급 한 사인 알고리즘을 사용하여 원형 입출력 루프를 구현합니다.

$ echo 1 >file
$ tail -f file | while read n; do echo "1+s(3*$n)" | bc -l; sleep 1; done | tee -a file
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
1.75155632167982146959
[..snip...]

여기 bc에서 부동 소수점 수학 s(...)을 수행하고 사인 함수에 대한 bc의 표기법입니다.

대신 변수를 사용하여 동일한 알고리즘을 구현

이 특정 수학 예제의 경우 순환 I / O 접근 방식이 필요하지 않습니다. 변수를 간단히 업데이트 할 수 있습니다.

$ n=1; while true; do n=$(echo "1+s(3*$n)" | bc -l); echo $n; sleep 1; done
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
[..snip...]

12

로 만든 FIFO를 사용할 수 있습니다 mkfifo. 그 것을 그러나주의 매우 쉽게 실수로 교착 상태를 만들 수 있습니다. 가설적인 "원형"예를 들어 설명해 드리겠습니다. 명령의 출력을 입력에 공급합니다. 교착 상태가 발생할 수있는 두 가지 방법이 있습니다.

  1. 이 명령에는 출력 버퍼가 있습니다. 부분적으로 채워져 있지만 아직 실제로 플러시되지 않았습니다. 일단 채워지면 그렇게 할 것입니다. 입력을 다시 읽습니다. 대기중인 입력이 실제로 출력 버퍼에 있기 때문에 영원히 앉아있을 것입니다. 그리고 그 입력을 얻을 때까지 플러시되지 않습니다 ...

  2. 이 명령에는 많은 출력이 있습니다. 쓰기를 시작하지만 커널 파이프 버퍼가 채워집니다. 버퍼에 공간이 생길 때까지 기다립니다. 그것은 입력을 읽 자마자 발생합니다. 즉, 출력에 아무것도 쓰지 않을 때까지는 그렇게하지 않을 것입니다.

그것은 당신이 그것을하는 방법입니다. 이 예제는 with od로 끝나지 않는 16 진 덤프 체인을 만듭니다.

mkfifo fifo
( echo "we need enough to make it actually write a line out"; cat fifo ) \ 
    | stdbuf -i0 -o0 -- od -t x1 | tee fifo

결국 중지됩니다. 왜? 위의 # 2 교착 상태입니다. stdbuf버퍼링을 비활성화하기 위해 호출이 있음을 알 수 있습니다 . 그것없이? 출력이없는 교착 상태


고마워, 나는이 맥락에서 버퍼에 대해 아무것도 몰랐다. 그것에 대해 더 자세히 읽을 키워드가 있습니까?
Abdul Al Hazred

1
@AbdulAlHazred 입출력 버퍼링에 대해서는 stdio 버퍼링을 찾아보십시오 . 파이프의 커널 버퍼의 경우 파이프 버퍼 가 작동하는 것 같습니다.
derobert

4

일반적으로 Makefile (명령 make)을 사용하고 다이어그램을 makefile 규칙에 매핑하려고합니다.

f1 f2 : f0
      command < f0 > f1 2>f2

반복적 / 순환 명령을 사용하려면 반복 정책을 정의해야합니다. 와:

SHELL=/bin/bash

a.out : accumulator
    cat accumulator <(date) > a.out
    cp a.out accumulator

accumulator:
    touch accumulator     #initial value

각각 make은 한 번에 하나의 반복을 생성합니다.


의 남용 make이지만 불필요합니다. 중간 파일을 사용하는 경우 루프를 사용하여 파일을 관리하지 않는 이유는 무엇입니까?
Alexis

@alexis, makefiles는 아마 과잉 일 것입니다. 나는 루프에 대해 매우 편안하지 않습니다 : 나는 시계의 개념, 정지 상태 또는 명확한 예를 놓칩니다. 초기 다이어그램은 워크 플로와 함수 시그니처를 기억했습니다. 복잡한 다이어그램의 경우 데이터 연결 또는 makefile 형식 규칙이 필요합니다. (이것은
끔찍한

@alexis는 물론 동의합니다.
JJoao

나는 이것이 남용이라고 생각하지 않습니다- 매크로make 에 관한 것입니다. 여기서 완벽한 응용 프로그램입니다.
mikeserv

1
@ mikeserv입니다. 그리고 우리는 모두 학대 도구가 Unix의 지하 Magna Carta라는 것을 알고 있습니다 :)
JJoao

4

알다시피, 다이어그램이 묘사 할 때 반복적 인 피드백 루프가 필요하다는 것을 확신하지는 못합니다. 아마도 coprocesses 사이에 지속적인 파이프 라인을 사용할 수 있습니다 . 그런 다음 다시 큰 차이가 없을 수 있습니다. 일단 코 프로세스에서 행을 열면 일반적인 스타일 루프를 구현하여 일반적인 정보를 작성하지 않고 정보를 쓰고 정보를 읽습니다.

우선, 그것은 bc당신을위한 공동 프로세스의 주요 후보입니다. 에서 bc당신이 할 수있는 define귀하의 의사에 꽤 많이 물어 무엇을 할 수있는 기능입니다. 예를 들어,이를 수행하는 매우 간단한 함수는 다음과 같습니다.

printf '%s()\n' b c a |
3<&0 <&- bc -l <<\IN <&3
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
IN

... 인쇄 할 것이다 ...

b=3
c=.14112000805986722210
a=1.14112000805986722210

그러나 물론 지속 되지 않습니다 . printf파이프 를 담당하는 서브 쉘 이 종료 되 자마자 (printfa()\n 파이프에 직후 ) 파이프가 끊어지고 bc입력이 닫히고 종료됩니다. 그것은 거의 유용하지 않습니다.

@derobert는 이미 유틸리티를 사용하여 명명 된 파이프 파일을 만들어서 FIFO를 언급했습니다 . 시스템 커널은 파일 시스템 항목을 양쪽 끝에 연결한다는 점을 제외하면 본질적으로 파이프이기도합니다. 이것들은 매우 유용하지만 파일 시스템에서 스누핑 될 위험없이 파이프를 가질 수 있다면 더 좋을 것입니다.mkfifo

그것이 일어날 때, 쉘은 이것을 많이합니다. 프로세스 대체 를 구현하는 쉘을 사용하는 경우 , 통신 할 수있는 백그라운드 프로세스에 지정할 수 있는 지속적인 파이프 를 얻을 수있는 매우 간단한 방법 이 있습니다.

bash를 들어, 프로세스 대체가 어떻게 작동하는지 확인할 수 있습니다.

bash -cx ': <(:)'
+ : /dev/fd/63

당신은 그것이 실제로 대체 인 것을 본다 . 쉘은 확장하는 동안 파이프 링크 에 대한 경로에 해당하는 값을 대체합니다 . 이를 활용할 수 있습니다. 대체 자체 내에서 실행되는 프로세스와 통신하기 위해 해당 파이프를 사용하도록 제한 할 필요는 없습니다 ...()

bash -c '
    eval "exec 3<>"<(:) "4<>"<(:)
    cat  <&4 >&3  &
    echo hey cat >&4
    read hiback  <&3
    echo "$hiback" here'

... 인쇄 ...

hey cat here

이제는 다른 쉘이 다른 방식으로 코 프로세스를 수행한다는 것을 알고 있습니다. 그리고 bash하나를 설정하기위한 특정 구문이 있습니다 (아마도 하나를위한 zsh것입니다) . 난 그냥 당신이 모두에서 두서없는 장문의 모든없이 거의 같은 일을 위의 구문을 사용할 수 있다는 것을 알고 bash그리고 zsh- 당신은에 매우 비슷한 일을 할 수 dashbusybox ash여기-문서와 같은 목적을 달성하기 위해 (때문에 dash그리고 busybox여기 - 할 다른 두 가지처럼 임시 파일이 아닌 파이프가있는 문서) .

따라서에 적용하면 bc...

eval "exec 3<>"<(:) "4<>"<(:)
bc -l <<\INIT <&4 >&3 &
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
INIT
export BCOUT=3 BCIN=4 BCPID="$!"

... 그것은 어려운 부분입니다. 그리고 이것은 재미있는 부분입니다 ...

set --
until [ "$#" -eq 10 ]
do    printf '%s()\n' b c a >&"$BCIN"
      set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"

... 인쇄 ...

b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965

... 그리고 여전히 실행 중입니다 ...

echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"

... 단지 나에게의 마지막 값을 얻을 수있는 bc의를 a대신 호출하는 대신 a()IT 및 인쇄를 증가시키는 기능을 ...

.29566669586771958965

실제로 그것을 죽이고 IPC 파이프를 찢을 때까지 계속 실행됩니다 ...

kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT

1
매우 흥미로운. 최근 bash 및 zsh를 사용하면 파일 디스크립터를 지정할 필요가 없습니다. 예를 들면 다음 eval "exec {BCOUT}<>"<(:) "{BCIN}<>"<(:)과 같습니다.
Thor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.