2012-02-19 3 views
3

bash 스크립트에 사용자 정의 로깅 기능을 추가했는데, 하나의 명명 된 파이프에서 출력을 가져 와서 다른 명명 된 파이프로 다시 공급하지 않는 이유를 알 수 없습니다. $PIPE_LOG하나의 명명 된 파이프에서 다른 명명 된 파이프로 다시 출력 할 수 있습니까?

#!/bin/bash 

PROGNAME=$(basename $(readlink -f $0)) 
LOG="$PROGNAME.log" 
PIPE_LOG="$PROGNAME-$$-log" 
PIPE_ECHO="$PROGNAME-$$-echo" 

# program output to log file and optionally echo to screen (if $1 is "-e") 
log() { 
    if [ "$1" = '-e' ]; then 
    shift 
    [email protected] > $PIPE_ECHO 2>&1 
    else 
    [email protected] > $PIPE_LOG 2>&1 
    fi 
} 

# create named pipes if not exist 
if [[ ! -p $PIPE_LOG ]]; then 
    mkfifo -m 600 $PIPE_LOG 
fi 
if [[ ! -p $PIPE_ECHO ]]; then 
    mkfifo -m 600 $PIPE_ECHO 
fi 

# cat pipe data to log file 
while read data; do 
    echo -e "$PROGNAME: $data" >> $LOG 
done < $PIPE_LOG & 

# cat pipe data to log file & echo output to screen 
while read data; do 
    echo -e "$PROGNAME: $data" 
    log echo $data # this doesn't work 
    echo -e $data > $PIPE_LOG 2>&1 # and neither does this 
    echo -e "$PROGNAME: $data" >> $LOG # so I have to do this 
done < $PIPE_ECHO & 

# clean up temp files & pipes 
clean_up() { 
    # remove named pipes 
    rm -f $PIPE_LOG 
    rm -f $PIPE_ECHO 
} 
#execute "clean_up" on exit 
trap "clean_up" EXIT 

log echo "Log File Only" 
log -e echo "Echo & Log File" 

내가 34 에서 $data을 할 줄에 명령을 생각 출력이 : 여기

은 기본 스크립트 버전 ( http://pastebin.com/RMt1FYPc)입니다. 하지만 작동하지 않습니다. 대신 출력을 $PIPE_LOG을 거치지 않고 직접 로그 파일로 보내야합니다.

내가 예상 한대로 작동하지 않는 이유는 무엇입니까?

편집 : 나는 "bash"로 shebang을 변경했습니다. 그러나 문제는 동일합니다.

해결 방법 : A.H.'s answer은 명명 된 파이프를 올바르게 사용하지 않는다는 것을 알았습니다. 이후로 명명 된 파이프를 사용하지 않아서 문제가 해결되었습니다. 그 해결책은 여기에 있습니다 : http://pastebin.com/VFLjZpC3

+0

당신은 bash 스크립트라고 말하면서 스크립트의 첫번째 줄에'/ bin/sh'를 지정하고 있습니다. bash에서이 작업을 수행해야하는지 확실합니까? bash는 기본적으로 모든 운영 체제에 설치되지 않습니다. – ghoti

+0

잠재적 인 문제는이 스크립트를 bash [경로] 대신 경로를 사용하여 실행하면 bash의 "sh"버전을 호출한다는 것입니다 (동일한 실행 파일 일지라도). 작동하지 않습니다. 정확히 같은 (제한). – huelbois

+0

"sh"를 "bash"로 바꾸려고 시도했지만 같은 방식으로 작동합니다 (즉, 그렇지 않습니다). – kipkoan

답변

9

, 명명 된 파이프가 정말 무엇인지 이해하지 않는다 ... 그것은 도움이되기를 바랍니다. 명명 된 파이프가 이 아니고 일반적인 파이프처럼 스트림입니다. 명명 된 파이프를 닫을 수 있고 생산자 측의 닫기가 일 수 있으므로 소비자 측에 닫기로 표시된 일 수 있으므로 보통 파이프의 시리즈 일 수 있습니다.

일 수 있습니다. 소비자는 더 이상 데이터가 없을 때까지 데이터를 읽습니다. 더 이상 데이터가 없다는 것을 의미합니다. read 호출시 명명 된 파이프가 열려 있지 않습니다. 즉, 한 명 이상의 생산자가없는 시점에 여러 생산자가 한 소비자에게만 피드를 제공 할 수 있습니다. 자동으로 닫히는 문을 생각해보십시오. 문 손잡이를 다음 문으로 넘기거나 동시에 여러 사람을 쥐어 문을 열어 두는 사람이 꾸준히 문을 열면 문이 열립니다. 그러나 문이 닫히면 닫힌 채로 있습니다.

열기 세 개의 껍질 :

약간의 데모는 차이가 좀 명확하게해야한다. 우선 쉘 없음 출력 cat 때문에 도시되지

1> mkfifo xxx 
1> cat xxx 

명명 된 파이프를 개방하고 데이터를 기다리고있다.

둘째 쉘 :

2> cat > xxx 

없이 출력이 cat 우리가 명시 적으로 종료하라고 할 때까지 열린 명명 된 파이프를 유지 프로듀서이기 때문이다.

셋째 쉘 :

3> echo Hello > xxx 
3> 

이 생산 즉시 반환합니다.

먼저 쉘 : 소비자는 수신 된 데이터를

Hello 

는 그것을 쓴 - 하나 개 더 소비자가 문을 열어 유지하기 때문에, 대기하고 있습니다.

셋째 쉘

3> echo World > xxx 
3> 

먼저 쉘 : 소비자가, 수신 데이터를 쓰고

World 

- 또 하나 개의 소비자가 문을 열어 유지하기 때문에, 대기하고 있습니다.

두번째 셸 :

And good bye! 
(control-d key) 
2> 

먼저 쉘

^D 키 마지막 제조자의 cat > xxx 폐쇄
And good bye! 
1> 

, 따라서 값 : cat > xxx 창에 연락 소비자 이탈도.

  • 귀하의 log 기능을 열고 파이프를 여러 번 닫으려고합니다 의미 귀하의 경우


    . 별로 좋은 생각이 아닙니다.

  • while 루프가 모두 예상보다 빨리 종료됩니다. (확인하려면 (while ... done < $PIPE_X; echo FINISHED;) &
  • 다양한 생산자와 소비자의 일정에 따라 때로는 문이 닫히거나 때로는 문이 닫히지 않을 수도 있습니다. 경쟁 조건이 내장되어 있습니다. (테스트의 경우에는 sleep 1을 끝에 추가 할 수 있습니다. . log 기능)
  • 당신 "을 testcases은"한 번만 각 가능성을 시도 - 당신의 생산자가 모든 소비자를 찾을 수 있기 때문에, (당신이 특히 sleep들과, 차단) 그들에게 여러 번 사용을 시도
.

코드에서 문제를 설명 할 수 있지만 해결책을 말할 수는 없습니다. b 요구 사항의 가장자리가 무엇인지 명확하지 않습니다.

+0

위의 답변은 매우 명확합니다. 그러나 이상한 점은 RHEL 6.2와 Debian Squeeze에서 매우 다르게 동작하며 이유를 이해할 수 없다는 것입니다. RHEL에서 첫 번째 루프는 전혀 종료되지 않습니다 (읽기가 아닌 쓰기 대신에 붙어 있음). 데비안에서는 여러 로그를 보내는 것이 정상적으로 작동했지만 수면을 추가하면 모든 일이 멈추었습니다. 나는 그 주제에 대해 더 깊이 들어가고 싶다. – huelbois

+0

@huelbois : 필자가 작성한대로 : 그것은 'sleep'에 의해 명시 적으로 만들어지는 경쟁 조건이다. 생산자의 "꾸준한 흐름"이 없다면 소비자는 빠져 나가고 _ 다음 생산자는 차단할 것이다. . 수면이 없다면 "꾸준한 흐름"이 더 가능성이 있습니다. 또한 다른 OS 배포판은 다른 하드웨어를 나타내므로 다른 스케줄링 동작을 나타냅니다. –

+1

@huelbois : 필자는 내부 구조를 숨기려고 노력했지만 이제는 이름을 지정하는 것이 더 좋습니다. 일반 파이프와 FIFO (일명 명명 된 파이프) 사이의 중요한 차이점 중 하나는 _ 블로킹이 정상적으로 발생한다는 것입니다. 표준 파이프를 사용하면 . 찌르다. 'read' /'write' 호출을 차단합니다. 반면에 FIFO는 대개 소비자와 생산자 모두에게 __open__ 호출을 차단합니다! 둘 다 "만난"후에는 "열린"호출이 모두 반환되고 반대가됩니다. 그리고 찌르다. 그들의 작업을 계속하십시오 (다시 차단할 수도 있음). 이것은'open' 중에 동기화가 수행되고'read' /'write' 동안에 동기화가 수행되지 않는다는 것을 의미합니다. –

0

"cat pipe data to log file"부분에 문제가있는 것 같습니다.

보자 : "&"을 사용하여 백그라운드에 루프를 넣으면 두 번째 루프와 병렬로 실행해야한다는 의미입니다.

그러나 문제는 "&"도 필요하지 않습니다. 왜냐하면 FIFO에서 더 이상 데이터를 사용할 수 없게되면 while..read가 중지되기 때문입니다. (여전히 첫 번째 읽기 작업을하기 위해서는 먼저 몇 가지 작업을해야합니다). 더 이상 사용할 수있는 데이터가 없다면 다음 읽기는 멈추지 않습니다 (또 다른 문제가 발생할 수 있습니다 : 프로그램이 어떻게 멈 춥니 까?).

나는 읽는 동안 파일에서 사용할 수있는 데이터가 더 많은지 확인한 후 읽지 않는 경우 중지합니다.

이 샘플을 확인할 수 있습니다

mkfifo foo 
while read data; do echo $data; done < foo 

이 스크립트는 중단됩니다, 다른 쉘 (또는 첫 번째를 BG)에서 아무것도 쓸 때까지. 그러나 그것은 읽기가 작동하자마자 끝납니다.

편집 : RHEL 6.2에서 테스트 한 결과 (예 : 불량!).

문제는 스크립트 ("a"스크립트라고 가정 해 봅시다)를 실행 한 후에 "a"프로세스가 남아 있다는 것입니다. 그래서, 예, 어떤 식 으로든 제가 전에 쓴 것처럼 스크립트가 멈 춥니 다 (그때 생각했던 그 바보 같은 대답은 아닙니다 :)). 단 하나의 로그를 작성하는 경우를 제외하고는 (로그 파일이거나 echo 인 경우 작동합니다).

(PIPE_LOG에 쓰기 작업을 할 때마다 멈추고 매번 프로세스가 실행되는 PIPE_ECHO의 읽기 루프입니다.)

  • 한 줄 PIPE_LOG에서 읽어와 그 후, 루프가
  • 는 다음 두 번째 메시지로 전송 종료 :

    내가 여기에 몇 디버그 메시지를 추가하고, 한 내가 볼 것입니다 PIPE_LOG (PIPE_ECHO로부터받은 후), 프로세스는 PIPE_LOG =>에서 더 이상 읽지 않습니다.

ls -l/proc/[pid]/fd를 사용하면 fifo가 아직 열려 있지만 삭제 된 것을 볼 수 있습니다. 사실, 스크립트가 종료되고 제거되지만 아직 그것을 사용하는 프로세스가 하나 있습니다. 정리에서 log fifo를 제거하지 않고 고양이를 cat하면, 매달리기 과정이 해제됩니다.

이 날 것으로 보인다
+0

이 스크립트를 실행해볼 수 있습니다. 멈추지 않아. 문제는 34 번과 35 번 줄에서 로그 파일에 아무 것도 출력하지 않는다는 것입니다. – kipkoan

+0

네 말이 맞아. 미안. 그러나 오늘 읽는 동안과 fifo와 함께 무엇인가 배웠다! – huelbois

관련 문제