Bagaimana cara "menggabungkan" garis-garis yang dicetak oleh banyak program dengan aman?

11

Misalkan saya ingin menjalankan beberapa program secara paralel dan menggabungkan outputnya ke satu pipa:

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

Pendekatan shell ini berfungsi dengan baik untuk kasus sederhana ini, tetapi saya berharap akan gagal jika program menghasilkan garis yang lebih banyak dan lebih panjang dengan cara buffered, seperti ini (dibangun):

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

Salah satu solusi yang saya sarankan untuk digunakan adalah 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)

, tapi ini adalah opsi yang kurang optimal: ini mengeluarkan data dengan lambat, tidak berhenti; Saya melihat output tidak dalam urutan "sleep", tetapi dalam urutan argumen dalam kasus ini:

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

Saya sudah mengimplementasikan program kecil khusus untuk ini, tetapi percaya bahwa harus ada beberapa cara standar yang baik untuk melakukannya.

Bagaimana cara melakukannya menggunakan alat standar (dan tanpa tail -fkerugian)?

Vi.
sumber
Bagaimana Anda ingin mencampur output? Rupanya Anda ingin mencampur output karena Anda ingin "urutan tidur" daripada "urutan argumen". Apakah kebutuhan Anda untuk mencampur output tetapi bukan garis, yaitu untuk setiap baris dicetak secara atomis?
Gilles 'SANGAT berhenti menjadi jahat'
Juga. Semua baris dari semua program yang dimulai harus dikirim lebih awal, tetapi tanpa pencampuran di dalam setiap baris.
Vi.
Saya pikir cara standar untuk melakukan ini disebut, yah, syslog...
Shadur
Apakah menggunakan syslogbukan untuk log, tetapi untuk sesuatu yang menurut kebiasaan dianggap OK?
Vi.
Ini tidak lebih ideal daripada saran lain yang diposting sejauh ini, tapi saya pikir akan layak menyebutkan -sopsi untuk ekor. misalnya tail -f -s .1 fileakan mengurangi penundaan loop menjadi 0,1 detik dari default 1 detik.
cpugeniusmv

Jawaban:

3

GNU Parallel.

Dari catatan rilis tertanggal Agustus 2013:

--line-bufferakan menyangga output secara garis. --groupmenjaga output bersama untuk seluruh pekerjaan. --ungroupmemungkinkan output untuk mixup dengan setengah garis yang berasal dari satu pekerjaan dan setengah garis yang datang dari pekerjaan lain. --line-buffercocok di antara keduanya; itu mencetak garis penuh, tetapi akan memungkinkan untuk mencampur garis pekerjaan yang berbeda.

Sebagai contoh:

parallel --line-buffer <jobs

Dimana jobsberisi:

./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

Keluaran:

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
cpugeniusmv
sumber
1

Solusi yang menerapkan kunci:

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

Seharusnya ada cara yang lebih cepat untuk membuat kunci daripada menggunakan sistem file.

Emmanuel
sumber
0

Saya tidak dapat memikirkan sesuatu yang sederhana, yang akan membantu Anda, jika dialog Anda terlalu panjang, bahwa satu program akan dikirim untuk tidur sebelum dapat, untuk menyelesaikan menulis baris ke stdout.

Namun, jika baris Anda cukup pendek untuk ditulis seluruhnya sebelum proses beralih, dan masalah Anda adalah, bahwa menghasilkan satu baris membutuhkan waktu sangat lama, Anda dapat buffer output menggunakan read.

Misalnya:

((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput
xwst
sumber
Tidak cantik. Tidak mungkin itu bisa diandalkan. Tidak mungkin kinerja itu bagus.
Vi.
Kamu benar. Itu tidak cantik tetapi lebih mirip hack kotor. Namun, saya pikir itu tidak cukup untuk menilai kinerja dan keandalan. Selain itu, Anda ingin menggunakan 'alat standar'. Jadi saya tidak akan terkejut, jika Anda harus menerima keburukan (pada akhirnya). Tetapi mungkin seseorang memiliki solusi yang lebih memuaskan.
xwst
Saat ini saya puas dengan program saya (tertaut dalam pertanyaan) kecuali bahwa itu tidak tersedia di repositori sehingga tidak dapat dianggap bahkan sedikit "standar". Solusinya mungkin dengan mencoba mendorongnya ke sana ...
Vi.
0

Anda dapat membuat pipa bernama dengan mkfifo, membuang semua output ke pipa bernama, dan membaca secara terpisah dari pipa bernama untuk data yang dikumpulkan:

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

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

wait; wait; wait; wait
DopeGhoti
sumber
1
Bagaimana ini akan melindungi dari mangling kapan job1dan job2menghasilkan garis panjang (> 4096 byte)? Ini tampaknya dinamai pipa yang sama dengan contoh pertama kode dalam pertanyaan.
Vi.
Titik yang sangat adil. Saya tidak mempertimbangkan output gumpalan besar meskipun itu dipanggil secara eksplisit dalam pertanyaan Anda. Saya sekarang bertanya-tanya apakah mungkin tidak ada alat yang melakukan kebalikan dari tee, yang terdengar persis seperti yang Anda inginkan. Mungkin melihat internal syslogatau alat logging lainnya, karena mereka pasti mengumpulkan output dari beberapa tempat menjadi satu file log. Mengunci mungkin merupakan jawaban yang tepat, seperti yang disarankan @emmanual, juga.
DopeGhoti