Làm thế nào để kết hợp các dòng chữ được in bởi nhiều chương trình một cách an toàn?


11

Giả sử tôi muốn thực hiện song song nhiều chương trình và kết hợp đầu ra của chúng thành một ống:

sh -c '
    (echo qqq; echo qqq2; echo qqq3)&
    (echo www; echo www2; echo www3)& 
    (echo eee; echo eee2; echo eee3)& 
  wait; wait; wait'

Cách tiếp cận shell này hoạt động tốt cho trường hợp đơn giản này, nhưng tôi hy vọng nó sẽ thất bại nếu các chương trình xuất ra nhiều dòng hơn và dài hơn theo cách được đệm, như thế này (được xây dựng):

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

Một trong những giải pháp tôi được gợi ý sử dụng là tail -f:

tail -n +0 -q -f <(echo qqq; echo qqq2; echo qqq3) <(echo www; echo www2; echo www3) <(echo eee; echo eee2; echo eee3)

, nhưng đây là tùy chọn tối ưu phụ: nó xuất dữ liệu chậm chạp, nó không chấm dứt; Tôi thấy các kết quả đầu ra không theo thứ tự "ngủ", nhưng theo thứ tự đối số trong trường hợp này:

tail -n +0 -q -f <(sleep 1; echo qqq; sleep 1; echo qqq2; echo qqq3) <(echo www; echo www2; sleep 10; echo www3) <(echo eee; sleep 4; echo eee2; echo eee3) | cat

Tôi đã thực hiện một chương trình nhỏ đặc biệt cho việc này, nhưng tin rằng nên có một số cách tốt để làm điều đó.

Làm thế nào để làm điều đó bằng cách sử dụng các công cụ tiêu chuẩn (và không có tail -fbất lợi)?


Làm thế nào để bạn muốn trộn đầu ra? Rõ ràng là bạn muốn trộn lẫn kết quả đầu ra vì bạn muốn thứ tự giấc ngủ của Google chứ không phải là đối số của đơn hàng. Là yêu cầu của bạn để trộn đầu ra nhưng không phải là các dòng, tức là phải có mỗi dòng được in nguyên tử?
Gilles 'SO- ngừng trở nên xấu xa'

Đường thẳng. Tất cả các dòng từ tất cả các chương trình bắt đầu nên được phân phối sớm, nhưng không trộn lẫn bên trong mỗi dòng.
Vi.

Tôi nghĩ rằng cách tiêu chuẩn để làm điều này được gọi là, ờ, syslog...
Shadur

Là sử dụng syslogkhông phải cho nhật ký, nhưng cho một cái gì đó tùy chỉnh được coi là OK?
Vi.

Điều này không lý tưởng hơn bất kỳ đề xuất nào được đăng cho đến nay, nhưng tôi nghĩ rằng nó đáng để đề cập đến -stùy chọn cho đuôi. ví dụ: tail -f -s .1 filesẽ giảm độ trễ vòng lặp xuống còn 1 giây so với 1 giây mặc định.
cpugeniusmv

Câu trả lời:


3

GNU song song.

Từ ghi chú phát hành ngày tháng 8 năm 2013:

--line-buffersẽ đệm đầu ra trên cơ sở dòng. --groupgiữ đầu ra cùng nhau cho toàn bộ công việc. --ungroupcho phép đầu ra để trộn lẫn với một nửa dòng đến từ một công việc và một nửa dòng đến từ một công việc khác. --line-bufferphù hợp giữa hai điều này; nó in một dòng đầy đủ, nhưng sẽ cho phép trộn các dòng công việc khác nhau.

Ví dụ:

parallel --line-buffer <jobs

Nơi jobschứa:

./long.sh
./short.sh one
./short.sh two

short.sh:

#!/bin/bash

while true; do
        echo "short line $1"
        sleep .1
done

long.sh:

#!/bin/bash

count=0
while true; do
        echo -n "long line with multiple write()s "
        sleep .1
        count=$((count+1))
        if [ $count -gt 30 ]; then
                count=0
                echo
        fi
done

Đầu ra:

short line one
short line two
short line one
short line two
short line one
**-snip-**
short line one
short line one
short line two
short line two
short line one
short line one
short line one
long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s 
short line two
short line two
short line two
short line one

1

Một giải pháp thực hiện khóa:

function putlines () {
   read line || return $?
   while ! ln -s $$ lock >/dev/null 2>&1
   do
      sleep 0.05
   done
   echo "$line" 
}

function getlines () {
     while read lline
     do 
          echo "$lline"
          rm lock
     done
}

# your paralelized jobs  
(  
   job1 | putlines & 
   job2 | putlines & 
   job3 | putlines & 
   wait
) | getlines| final_processing

Cần có một cách nhanh hơn để tạo khóa hơn là sử dụng hệ thống tập tin.


0

Tôi không thể nghĩ bất cứ điều gì đơn giản, điều đó sẽ giúp bạn, nếu các dòng của bạn quá dài, một chương trình sẽ được gửi đến chế độ ngủ trước khi có thể, để hoàn thành việc viết một dòng lên thiết bị xuất chuẩn.

Tuy nhiên, nếu các dòng của bạn đủ ngắn để được viết hoàn toàn trước khi chuyển đổi quy trình và vấn đề của bạn là, việc tạo một dòng mất rất nhiều thời gian, bạn có thể đệm đầu ra bằng cách đọc.

Ví dụ:

((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput

Không đẹp. Không chắc là đáng tin cậy. Không chắc rằng hiệu suất sẽ tốt.
Vi.

Bạn đúng. Nó không đẹp nhưng trông giống như một bản hack bẩn thỉu. Tuy nhiên, tôi không nghĩ rằng điều đó đủ để đánh giá hiệu suất và độ tin cậy. Ngoài ra, bạn muốn sử dụng 'công cụ tiêu chuẩn'. Vì vậy, tôi sẽ không ngạc nhiên, nếu bạn phải chấp nhận một số điều xấu xí (cuối cùng). Nhưng có lẽ ai đó có một giải pháp thỏa đáng hơn.
xwst

Hiện tại tôi hài lòng với chương trình của mình (được liên kết trong câu hỏi) ngoại trừ việc nó không có sẵn trong kho, do đó không thể được coi là một "tiêu chuẩn" nhỏ. Giải pháp có thể là cố gắng đẩy nó đến đó ...
Vi.

0

Bạn có thể tạo một ống có tên với mkfifo, đổ tất cả đầu ra vào ống được đặt tên và đọc riêng từ ống được đặt tên cho dữ liệu được thu thập của bạn:

mkfifo /tmp/mypipe
job1 > /tmp/mypipe &
job2 > /tmp/mypipe &
job3 > /tmp/mypipe &

cat /tmp/mypipe > /path/to/final_output &

wait; wait; wait; wait

1
Làm thế nào điều này sẽ bảo vệ khỏi xáo trộn khi job1job2đầu ra các dòng dài (> 4096 byte)? Điều này dường như được đặt tên ống tương đương với ví dụ đầu tiên về mã trong câu hỏi.
Vi.

Điểm rất công bằng. Tôi đã không xem xét sản lượng blob lớn mặc dù nó được gọi rõ ràng trong câu hỏi của bạn. Bây giờ tôi tự hỏi liệu có lẽ không có công cụ nào làm ngược lại tee, nghe có vẻ chính xác như những gì bạn muốn. Có thể nhìn vào phần bên trong sysloghoặc các công cụ ghi nhật ký khác, vì chúng chắc chắn tổng hợp đầu ra từ một vài nơi thành một tệp nhật ký. Khóa cũng có thể là câu trả lời đúng, như @emmanual đề xuất, cũng có.
DopeGhoti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.