명명된 파이프를 사용하여 bash 파이프 "작성" 함수를 작성하는 방법

명명된 파이프를 사용하여 bash 파이프 "작성" 함수를 작성하는 방법

이 페이지함수 생성을 compose위한 의사 코드가 자세히 설명되어 있습니다.N명령을 내리고 파이프라인에서 실행합니다.

compose우리는 다음과 같은 명령을 작성합니다 .

compose cmd1 cmd2 ... cmdn

이는 쉘 명령처럼 작동합니다.

cmd1 | cmd2 | ... | cmdn

나는 노력하고있다명명된 파이프저는 compose실제로 Bash로 글을 쓰는 데 관심이 있습니다. 불행하게도 이 작업을 수행하면 출력이 전혀 나오지 않습니다. 아마도 다른 파이프에 대한 읽기 및 쓰기 경쟁 조건으로 인해 발생한 것 같습니다. 여러 번 반복했는데 계속 혼란스러운 동작이 발생합니다. 나는 그것을 이 작은 문제로 축소했습니다.

echo foo |   # stdin
{
  mkfifo p   # create pipe p
  cat > p &  # direct stdin to pipe p
  cat < p    # read pipe p to stdout
  rm p       # remove pipe p
}

이 출력을 원 foo하지만 아무것도 얻지 못합니다. 내가 뭘 잘못했나요?

답변1

질문에 있는 예제 코드의 문제는 미묘합니다. 명명된 파이프에 쓰려면 &명령을 백그라운드에 두어야 합니다. 그렇지 않으면 읽기를 기다리는 것이 차단됩니다. 그런데 이렇게 하면 "백그라운드에서 시작된 명령은 &표준 입력이 [to] 로 리디렉션됩니다 /dev/null."는 표준 입력이 아닌 /dev/null파이프를 통해 입력된 내용을 의미합니다.p

Bash에서 솔루션은 간단합니다. stdin을 백그라운드 프로세스로 리디렉션합니다 0<&0. 그러면 예제가 제대로 작동할 것입니다.

$ echo foo | { mkfifo p; cat 0<&0 > p & cat < p; rm p; }
foo

전체 compose함수는 다음과 같이 표시됩니다.

compose() {
  dir=$(mktemp -d)                 # Create a temp dir to hold the pipes
  cd $dir                          #   to avoid filename conflicts
  i=0                              #
  mkfifo "pipe$i"                  # Create pipe0, the output for command $1
  ($1 0<&0 > "pipe$i" &)           # Start $1, reading stdin and writing to pipe0
  shift                            # Shift off $1 since it's running
    for c in "$@"; do              # Loop over the remaining commands
    ii=$((i+1))                    # 
    mkfifo "pipe$ii"               # Create a pipe i+1, the next command's output
    ($c < "pipe$i" > "pipe$ii" &)  # Start the next command, reading from the
    i=$ii                          #   i'th pipe and writing to the i+1'th
  done                             #
  cat "pipe$i"                     # Output the last pipe, executing the commands
  cd - > /dev/null                 # Change back to the old directory
  rm -rf $dir                      # Remove all the pipes
}

답변2

compose()
    case    $#  in  
    [01])  "$@" ;;          ## if 1 or fewer args just run what we've got
       *)  "$1" | {         ## otherwise pipe output from $1 into a
    shift; compose "$@"     ## self-call until all args are gone
};  esac

관련 정보