2008-09-02 58 views
19

Giả sử rằng tôi có các chương trình P0, P1, ... P(n-1) đối với một số n > 0. Làm thế nào tôi có thể dễ dàng chuyển hướng đầu ra của chương trình Pi sang chương trình P(i+1 mod n) cho tất cả i (0 <= i < n)? Ví dụ, giả sử tôi có một chương trình square, liên tục đọc một số và in hình vuông của số đó, và một chương trình calc, đôi khi in một số sau đó nó hy vọng có thể đọc được hình vuông của nó. Làm cách nào để kết nối các chương trình này sao cho bất cứ khi nào calc in một số, các ô vuông square nó trả lại nó thành calc?Cách tạo vòng lặp ống trong bash

Chỉnh sửa: Tôi có lẽ nên làm rõ ý tôi là "dễ dàng". Giải pháp ống/FIFO được đặt tên là một giải pháp thực sự hoạt động (và tôi đã sử dụng trong quá khứ), nhưng nó thực sự đòi hỏi khá nhiều công việc để làm đúng nếu bạn so sánh nó bằng cách sử dụng một đường ống bash. (Bạn cần lấy tên tệp chưa tồn tại, tạo một đường ống với tên đó, chạy "vòng lặp ống", làm sạch đường ống có tên.) Hãy tưởng tượng bạn không còn có thể viết prog1 | prog2 và luôn phải sử dụng các đường ống được đặt tên để kết nối các chương trình .

Tôi đang tìm một thứ gần như dễ dàng như viết một ống "bình thường". Ví dụ một cái gì đó như { prog1 | prog2 } >&0 sẽ là tuyệt vời.

Trả lời

5

Đây là một câu hỏi rất thú vị. Tôi (mơ hồ) nhớ một bài tập rất giống trong trường đại học cách đây 17 năm. Chúng tôi đã phải tạo ra một loạt các đường ống, trong đó mã của chúng tôi sẽ nhận được các tập tin cho đầu vào/đầu ra của mỗi ống. Sau đó, các mã sẽ ngã ba và đóng các tập tin không sử dụng.

Tôi nghĩ bạn có thể làm điều gì đó tương tự với các đường ống có tên trong bash. Sử dụng mknod hoặc mkfifo để tạo một tập hợp các đường ống với các tên duy nhất mà bạn có thể tham khảo rồi nĩa chương trình của bạn.

-1

Tôi nghi ngờ sh/bash có thể làm điều đó. ZSH sẽ là một cược tốt hơn, với các tính năng MULTIOS và coproc của nó.

+1

Bạn có thể đưa ra ví dụ về Zsh không? Tôi quan tâm đến nó. –

1

Ống có tên.

Tạo một loạt các FIFOs, sử dụng mkfifo

tức fifo0, fifo1

Sau đó, đính kèm mỗi quá trình trong nhiệm kỳ tới các đường ống bạn muốn:

processn < fifo (n-1)> fifon

13

Một ống được đặt tên có thể làm điều đó:

$ mkfifo outside 
$ <outside calc | square >outside & 
$ echo "1" >outside ## Trigger the loop to start 
+0

Bạn có thể giải thích dòng " outside &"? Tôi không chắc chắn về bên ngoài. –

+0

Chúng là các chuyển hướng shell tiêu chuẩn - đọc từ 'bên ngoài' và xuất ra 'bên ngoài'. bên ngoài là một năm mươi, vì vậy mọi thứ được viết cho nó, đi ra phía đọc. –

+0

Tôi đã thử mã này, nhưng nó không hoạt động. Dường như dòng này: ' bên ngoài & 'kết thúc ngay lập tức. – RnMss

24

Sau khi dành một chút thời gian ngày hôm qua cố gắng chuyển hướng stdout đến stdin, tôi đã kết thúc bằng phương pháp sau. Nó không thực sự tốt đẹp, nhưng tôi nghĩ tôi thích nó hơn là giải pháp ống/fifo có tên.

read | { P0 | ... | P(n-1); } >/dev/fd/0 

Các { ... } >/dev/fd/0 là để chuyển hướng stdout để stdin cho chuỗi ống như một toàn thể (ví dụ: nó chuyển hướng đầu ra của P (n-1) đến đầu vào của P0). Sử dụng >&0 hoặc một cái gì đó tương tự không hoạt động; điều này có thể là do bash giả định 0 là chỉ đọc trong khi nó không nhớ viết đến /dev/fd/0.

read -pipe ban đầu là cần thiết vì không có bộ mô tả tệp đầu vào và đầu ra là thiết bị tương tự (ít nhất trên hệ thống của tôi) và chuyển hướng không có hiệu lực. (Các thiết bị pts không hoạt động như một đường ống, viết cho nó đặt mọi thứ trên màn hình của bạn.) Bằng cách làm cho đầu vào của { ... } một đường ống bình thường, chuyển hướng có hiệu quả mong muốn.

Để minh họa với ví dụ calc/square tôi:

function calc() { 
    # calculate sum of squares of numbers 0,..,10 

    sum=0 
    for ((i=0; i<10; i++)); do 
    echo $i     # "request" the square of i 

    read ii     # read the square of i 
    echo "got $ii" >&2   # debug message 

    let sum=$sum+$ii 
    done 

    echo "sum $sum" >&2   # output result to stderr 
} 

function square() { 
    # square numbers 

    read j       # receive first "request" 
    while [ "$j" != "" ]; do 
    let jj=$j*$j 
    echo "square($j) = $jj" >&2 # debug message 

    echo $jj      # send square 

    read j      # receive next "request" 
    done 
} 

read | { calc | square; } >/dev/fd/0 

Chạy mã trên cho kết quả như sau:

square(0) = 0 
got 0 
square(1) = 1 
got 1 
square(2) = 4 
got 4 
square(3) = 9 
got 9 
square(4) = 16 
got 16 
square(5) = 25 
got 25 
square(6) = 36 
got 36 
square(7) = 49 
got 49 
square(8) = 64 
got 64 
square(9) = 81 
got 81 
sum 285 

Dĩ nhiên, phương pháp này là khá một chút của một hack. Đặc biệt là phần read có tác dụng phụ không mong muốn: việc chấm dứt vòng lặp ống "thực" không dẫn đến kết thúc toàn bộ. Tôi không thể nghĩ ra bất cứ điều gì tốt hơn so với read vì có vẻ như bạn chỉ có thể xác định rằng vòng lặp đường ống đã chấm dứt bằng cách cố gắng viết một cái gì đó cho nó.

+1

Giải pháp tốt. Tôi đã phải làm một cái gì đó tương tự như sử dụng netcat bên trong một vòng lặp và làm việc xung quanh tác dụng phụ 'đọc' bằng cách 'đóng' đầu vào của nó với một 'echo'. Cuối cùng nó là một cái gì đó như thế này: echo | đọc | {P0 | ... | P (n-1); }>/dev/fd/0 –

+0

Thay vì 'echo | read', người ta có thể sử dụng lệnh kết thúc ngay lập tức, như': '(' true'), ví dụ: ': | {cmd | cmd>/dev/fd/0} '.Ví dụ: ': | {nc -lp 5000>/dev/fd/0; } 'là một máy chủ echo đơn giản kết thúc chính xác trên máy khách EOF. – regnarg

-2

Ngăn xếp lệnh có thể được tạo thành chuỗi từ một dãy các lệnh tùy ý và được đánh giá bằng eval. Ví dụ sau đây cho kết quả 65536.

function square() 
{ 
    read n 
    echo $((n*n)) 
} # ---------- end of function square ---------- 

declare -a commands=('echo 4' 'square' 'square' 'square') 

#------------------------------------------------------------------------------- 
# build the command stack using pipes 
#------------------------------------------------------------------------------- 
declare  stack=${commands[0]} 

for ((COUNTER=1; COUNTER<${#commands[@]}; COUNTER++)); do 
    stack="${stack} | ${commands[${COUNTER}]}" 
done 

#------------------------------------------------------------------------------- 
# run the command stack 
#------------------------------------------------------------------------------- 
eval "$stack" 
+1

Tôi không nghĩ rằng bạn đang trả lời câu hỏi. – reinierpost

3

giải pháp của tôi sử dụng pipexec (Hầu hết các chức năng thực hiện xuất phát từ câu trả lời của bạn):

square.sh

function square() { 
    # square numbers 

    read j       # receive first "request" 
    while [ "$j" != "" ]; do 
    let jj=$j*$j 
    echo "square($j) = $jj" >&2 # debug message 

    echo $jj      # send square 

    read j      # receive next "request" 
    done 
} 

square [email protected] 

calc.sh

function calc() { 
    # calculate sum of squares of numbers 0,..,10 

    sum=0 
    for ((i=0; i<10; i++)); do 
    echo $i     # "request" the square of i 

    read ii     # read the square of i 
    echo "got $ii" >&2   # debug message 

    let sum=$sum+$ii 
done 

echo "sum $sum" >&2   # output result to stderr 
} 

calc [email protected] 

Lệnh

pipexec [ CALC /bin/bash calc.sh ] [ SQUARE /bin/bash square.sh ] \ 
    "{CALC:1>SQUARE:0}" "{SQUARE:1>CALC:0}" 

Sản lượng (giống như trong câu trả lời của bạn)

square(0) = 0 
got 0 
square(1) = 1 
got 1 
square(2) = 4 
got 4 
square(3) = 9 
got 9 
square(4) = 16 
got 16 
square(5) = 25 
got 25 
square(6) = 36 
got 36 
square(7) = 49 
got 49 
square(8) = 64 
got 64 
square(9) = 81 
got 81 
sum 285 

Comment: pipexec được thiết kế để bắt đầu quy trình và xây dựng đường ống tùy ý ở giữa. Vì các hàm bash không thể được xử lý như các tiến trình, nên cần có các hàm trong các tệp riêng biệt và sử dụng một bash riêng biệt.

Các vấn đề liên quan