Birden çok program tarafından yazdırılan satırları nasıl güvenli bir şekilde birleştirirsiniz?


11

Birden fazla programı paralel olarak yürütmek ve çıktılarını bir boruya birleştirmek istediğimi varsayalım:

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

Bu kabuk yaklaşımı bu basit durum için iyi çalışıyor, ancak programlar bu şekilde (inşa edilmiş) arabelleğe alınmış şekilde daha uzun satırlar çıkarırsa başarısız olmasını beklerim:

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

Kullanmak istediğim çözümlerden biri 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)

, ancak bu en iyi seçenek değildir: verileri yavaş bir şekilde çıkarır, sonlandırılmaz; Çıktıları "uyku" düzeninde değil, bu durumda argüman düzeninde görüyorum:

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

Bunun için özel bir küçük program uyguladım , ancak bunu yapmanın standart iyi bir yolu olması gerektiğine inanıyorum.

Standart araçlar kullanarak (ve tail -fdezavantajsız) nasıl yapılır?


Çıktıyı nasıl karıştırmak istersiniz? Görünüşe göre çıktıyı karıştırmak istiyorsunuz, çünkü “argüman sırası” yerine “uyku sırası” istiyorsunuz. Çıktıları karıştırma, ancak hatları karıştırmamanız mı gerekiyor, yani her satırın atomik olarak basılması mı?
Gilles 'SO- kötü olmayı bırak'

Linewise. Başlatılan tüm programlardan gelen tüm satırlar, her satırın içinde karıştırılmadan erken teslim edilmelidir.
Vi.

Bence bunu yapmanın standart yolu denir, iyi syslog...
Shadur

syslogGünlükler için değil, özel olarak kabul edilen bir şey için kullanmak tamam mı?
Vi.

Bu, şimdiye kadar yayınlanan diğer önerilerden daha ideal değil, ama -skuyruk seçeneğinden bahsetmeye değer olacağını düşündüm . Örneğin tail -f -s .1 file, döngü gecikmesini varsayılan 1 saniyeden 0,1 saniyeye düşürecektir.
cpugeniusmv

Yanıtlar:


4

GNU Paralel.

Gönderen sürüm notları Ağustos 2013 tarihli:

--line-buffersatır bazında çıktı arabelleğe alır. --grouptüm iş için çıktıyı bir arada tutar. --ungroupçıktının bir işten gelen yarım satır ve başka bir işten gelen yarım satır ile karışmasını sağlar. --line-bufferbu ikisi arasında uyuyor; tam bir çizgi yazdırır, ancak farklı işlerin çizgilerini karıştırmaya izin verir.

Örneğin:

parallel --line-buffer <jobs

Nerede jobsbulunur:

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

Çıktı:

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

Kilitleri uygulayan bir çözüm:

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

Kilit oluşturmanın dosya sistemini kullanmaktan daha hızlı bir yolu olmalıdır.


0

Basit bir şey düşünemiyorum, bu size yardımcı olacak, eğer çizgiler çok uzunsa, bir program mümkün olmadan önce uykuya gönderilecek, stdout'a bir satır yazmayı bitirecek.

Ancak, satırlarınız işlem değiştirmeden önce tamamen yazılacak kadar kısaysa ve sorununuz, bir satır oluşturmanın çok uzun sürmesi, okuma işlevini kullanarak çıktıyı arabelleğe alabilirsiniz.

Örneğin:

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

Güzel değil. Muhtemelen bu güvenilir değil. Performansın iyi olması muhtemel değildir.
Vi.

Haklısın. Güzel değil ama kirli bir hack gibi görünüyor. Ancak, performansı ve güvenilirliği değerlendirmek için yeterli olduğunu düşünmüyorum. Ayrıca, 'standart araçlar' kullanmak istediniz. Eğer çirkinliği kabul etmek zorunda kalırsanız (sonunda) şaşırmam. Ama belki birisinin daha tatmin edici bir çözümü vardır.
xwst

Şu anda programımdan memnun kaldım (soruya bağlı), ancak depolarda mevcut olmadığı için küçük bir "standart" bile kabul edilemez. Çözüm onu ​​oraya itmeye çalışmak olabilir ...
Vi.

0

İle adlandırılmış bir boru yapabilir mkfifo, tüm çıktıları adlandırılmış boruya dökebilir ve toplanan verileriniz için adlandırılmış borudan ayrı olarak okuyabilirsiniz:

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

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

wait; wait; wait; wait

2
Bu, uzun (> 4096 bayt) satırların çıkışını job1ve job2çıkışını nasıl yönetir ? Bu, sorgudaki ilk kodun boru eşdeğeri olarak adlandırılıyor gibi görünüyor.
Vi.

Çok adil bir nokta. Sorunuzda açıkça belirtilmesine rağmen büyük damla çıktısını düşünmedim. Şimdi tersini yapan tee, tam olarak istediğiniz gibi görünen bir araç olup olmadığını merak ediyorum . Muhtemelen syslogbirkaç yerden çıktıları tek bir günlük dosyasında topladıkları için dahili veya diğer günlük oluşturma araçlarına bakın. Kilitleme de @emmanual'ın önerdiği gibi doğru cevap olabilir.
DopeGhoti

0

Eski soru, biliyorum, Ama ben de aynı şeyi merak ediyordum, ve ben de bunu buldum:

garbling_job | (
    while read LINE
    do
        echo $LINE
    done
) &

Bozuk çıktı konusunda endişelenmeden bunlardan birkaçına başlayabiliyorum.

DÜZENLEME: Ole'nin önerdiği gibi - Uzun çizgilere dikkat etmelisiniz (> 4k, ayrıntılar için aşağıdaki Oles yorumuna bakın)

İşte benim test programım

if [ "$1" = "go" ]
then
for i in 1 2
do
    printf 111112222222222223333
    sleep .01
    printf 3333333444444444444555555555555
    sleep .01
    printf 6666666666666667777
    sleep .01
    printf 777777788888888889999999999999999
    sleep .01
    echo
done
exit
fi

# running them in sequence is all very fine
for i in 1 2 3 4 5 6 7 8
do
    echo bash $0 go 
done

# now this is all garbled up
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# using cat inbetween does not make it better
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go | cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# it does not help to use stdbuff after the thing that just printfs sporadicall
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go | stdbuf -oL cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# it does not help to use stdbuff before either - or I am not understanding stdbuff
for i in 1 2 3 4 5 6 7 8
do
    stdbuf -o10000 bash $0 go | stdbuf -oL cat &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# can I read - yes - they are now fine again
for i in 1 2 3 4 5 6 7 8
do
bash $0 go | (
    while read LINE
    do
        echo $LINE
    done
) &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done

1
Sayfa boyutunuzdan daha büyük satırlarla (genellikle 4-8K) test yapmanız gerekir. Bkz. Mywiki.wooledge.org/…
Ole Tange

Biraz katılmıyorum. Çok çeşitli çıktılarınız varsa, bir boruya tamamen çoğullama dışında bir şey yapmak isteyebilirsiniz. Eski moda günlükler ve durum mesajları gibi kısa satırlar için, yukarıdaki işe yarıyor gibi görünüyor ve çok basit.
user2692263

Çözümünüzde bir sınırlama olduğunu zaten biliyorsanız, bunu dahil etmeyi düşünün. UNIX çalışanlarıyla yaşadığım deneyim, eğer n için işe yarayan bir çözüm görürlerse o zaman n * 10 için de işe yarayacaklarını varsayarlar - n için bir sınır olmadığı açıklanmadığı sürece. Neden n için bir sınır olduğunu ve nasıl yükseltileceğini açıklayabilirseniz , daha da iyidir.
Ole Tange
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.