Birbirine bağlı komutlar arasında dairesel bir veri akışını nasıl uygulayabilirim?


19

Komutların birbirine nasıl bağlanabileceğini iki tür biliyorum:

  1. Pipe kullanarak (sonraki komutun std-çıkışını std-girişine koyarak).
  2. bir Tee kullanarak (çıkışı birçok çıkışa ekleyin).

Mümkün olanın bu olup olmadığını bilmiyorum, bu yüzden varsayımsal bir bağlantı türü çiziyorum:

resim açıklamasını buraya girin

Komutlar yerine değişkenler kullandığım örneğin bu sözde kod gibi komutlar arasında dairesel bir veri akışı uygulamak nasıl mümkün olabilir .:

pseudo-code:

a = 1    # start condition 

repeat 
{
b = tripple(a)
c = sin(b) 
a = c + 1 
}

Yanıtlar:


16

İle Uygulanan Dairesel I / O Döngüsü tail -f

Bu dairesel bir G / Ç döngüsünü uygular:

$ echo 1 >file
$ tail -f file | while read n; do echo $((n+1)); sleep 1; done | tee -a file
2
3
4
5
6
7
[..snip...]

Bu, bahsettiğiniz sinüs algoritmasını kullanarak dairesel giriş / çıkış döngüsünü uygular:

$ echo 1 >file
$ tail -f file | while read n; do echo "1+s(3*$n)" | bc -l; sleep 1; done | tee -a file
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
1.75155632167982146959
[..snip...]

Burada bckayan nokta matematiği var ve s(...)BC'nin sinüs fonksiyonu için gösterimi.

Değişken Kullanarak Aynı Algoritmanın Uygulanması

Bu özel matematik örneği için, dairesel G / Ç yaklaşımına gerek yoktur. Bir değişken basitçe güncellenebilir:

$ n=1; while true; do n=$(echo "1+s(3*$n)" | bc -l); echo $n; sleep 1; done
1.14112000805986722210
.72194624281527439351
1.82812473159858353270
.28347272185896349481
[..snip...]

12

Bunun için oluşturulmuş bir FIFO kullanabilirsiniz mkfifo. Ancak yanlışlıkla bir kilitlenme oluşturmanın çok kolay olduğunu unutmayın . Bunu açıklayayım - varsayımsal "dairesel" örneğinizi ele alalım. Bir komutun çıktısını girdisine beslersiniz. Bunun kilitlenmesinin en az iki yolu vardır:

  1. Komutun bir çıktı arabelleği vardır. Kısmen doldurulmuş, ancak henüz temizlenmemiş (aslında yazılmamış). Doldurduktan sonra bunu yapar. Böylece girdisini okumaya geri dönüyor. Orada sonsuza kadar oturacak, çünkü beklediği girdi aslında çıktı tamponunda. Ve bu girdiyi alana kadar temizlenmeyecek ...

  2. Komutun yazılacak bir sürü çıktısı var. Yazmaya başlar, ancak çekirdek boru tamponu dolar. Bu yüzden orada oturur, tamponda boşluk olmasını bekler. Bu, girdisini okuduktan sonra olur, yani çıkışına herhangi bir şey yazmayı bitirene kadar bunu asla yapmaz.

Dedi ki, işte böyle yapıyorsun. Bu örnek, odhiç bitmeyen bir altıgen döküm zinciri oluşturmak için:

mkfifo fifo
( echo "we need enough to make it actually write a line out"; cat fifo ) \ 
    | stdbuf -i0 -o0 -- od -t x1 | tee fifo

Sonunda durduğunu unutmayın. Neden? Kilitlendi, yukarıdaki # 2. stdbufArabelleğe almayı devre dışı bırakmak için oradaki aramayı da görebilirsiniz . O olmadan? Çıkışı olmayan kilitlenmeler.


teşekkürler, bu bağlamda tamponlar hakkında hiçbir şey bilmiyordum, bu konuda daha fazla okumak için bazı anahtar kelimeler biliyor musunuz?
Abdul Al Hazred

1
@AbdulAlHazred Giriş / çıkış arabelleğe alma için stdio arabelleğe alma konusuna bakın . Bir borudaki çekirdek tamponu için boru tamponu çalışıyor gibi görünüyor.
derobert

4

Genel olarak bir Makefile (komut make) kullanır ve diyagram kuralları yapmak için diyagramınızı eşlemeye çalışırım.

f1 f2 : f0
      command < f0 > f1 2>f2

Tekrarlayan / döngüsel komutlara sahip olmak için bir yineleme politikası tanımlamamız gerekir. İle:

SHELL=/bin/bash

a.out : accumulator
    cat accumulator <(date) > a.out
    cp a.out accumulator

accumulator:
    touch accumulator     #initial value

her makebiri bir seferde bir yineleme üretecektir.


Sevimli kötüye kullanım make, ancak gereksiz: Bir ara dosya kullanıyorsanız, neden sadece yönetmek için bir döngü kullanmıyorsunuz?
alexis

@alexis, makefiles muhtemelen aşırıya kaçmış. Döngüler hakkında çok rahat değilim: Saat, durma durumu veya açık bir örnek kavramını özlüyorum. İlk diyagramlar beni hatırladı İş akışları ve işlev imzaları. Karmaşık diyagramlar için veri bağlantılarına veya makefile yazılan kurallara ihtiyaç duyacağız. (bu sadece küfürlü bir sezgi)
JJoao

@alexis ve elbette, sana katılıyorum.
JJoao

Bunun kötüye kullanım olduğunu düşünmüyorum - burada mükemmel bir uygulama olan makrolarlamake ilgili .
mikeserv

1
@mikeserv, Evet. Ve hepimiz biliyoruz ki kötüye kullanım araçları Unix'in yeraltı Magna Carta :)
JJoao

4

Biliyorsunuz, diyagramlarınızın gösterdiği gibi mutlaka tekrar eden bir geri bildirim döngüsüne ihtiyacınız olduğuna ikna olmadım, belki bir yardımcı işlemler arasında kalıcı bir boru hattı . Sonra tekrar, çok fazla bir fark olmayabilir - bir coprocess üzerinde bir çizgi açtığınızda, sıradan bir şey yapmadan sadece bilgi yazma ve ondan bilgi okuma tipik stil döngülerini uygulayabilirsiniz.

İlk olarak, bcsizin için bir işbirliği süreci için birincil bir aday gibi görünecektir . İçindebcdefine pseudocode'unuzda istediğinizi yapabilen işlevler yapabilirsiniz. Örneğin, bunu yapmak için bazı çok basit işlevler şöyle görünebilir:

printf '%s()\n' b c a |
3<&0 <&- bc -l <<\IN <&3
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
IN

... yazdıracak ...

b=3
c=.14112000805986722210
a=1.14112000805986722210

Ama elbette, sürmez . En kısa sürede sorumlu alt kabuğa olarak printfbireyin boru kapanıyor (hemen sonraprintf yazma a()\nborusuna) boru yıkılıp edilir bc'ın giriş kapanır ve çok kapanıyor. Bu neredeyse olabildiğince yararlı değil.

@derobert zaten belirtildiği FIFO bir oluşturarak vardı edilebilir s adlandırılan boru dosyayı mkfifoprogramı. Bunlar, sistem çekirdeğinin bir dosya sistemi girişini her iki uca da bağlaması dışında, aslında sadece borulardır. Bunlar çok faydalıdır, ancak dosya sisteminde gözetleme riski olmadan bir boruya sahip olmanız daha iyi olurdu.

Olduğu gibi, kabuğunuz bunu çok yapar. İşlem ikamesi uygulayan bir kabuk kullanıyorsanız kalıcı bir boru elde etmek için çok basit bir yolunuz vardır - bu, iletişim kurabileceğiniz arka planlı bir sürece atayabileceğiniz türde.

İçinde bash Örneğin, nasıl işlem ikame işleri görebilirsiniz:

bash -cx ': <(:)'
+ : /dev/fd/63

Görüyorsunuz gerçekten bir ikame . Kabuk, genişleme sırasında bir boru bağlantısına giden yola karşılık gelen bir değer yerine geçer . Bundan faydalanabilirsiniz - bu boruyu yalnızca içinde hangi işlem yapılırsa kullanılsın iletişim kurmak için kısıtlamanız gerekmez.() ikame ...

bash -c '
    eval "exec 3<>"<(:) "4<>"<(:)
    cat  <&4 >&3  &
    echo hey cat >&4
    read hiback  <&3
    echo "$hiback" here'

... hangi baskı ...

hey cat here

Şimdi farklı kabuklar yapmak biliyoruz yardımcı işlemci farklı şekillerde şey - ve belirli bir sözdizimi olduğunu bashbirini kurmak için (ve muhtemelen bir için zshde) - ama nasıl böyle şeyler işi bilmiyorum. Sadece hem tekerleme tüm olmadan hemen hemen aynı şeyi yapmak için yukarıdaki sözdizimi kullanabileceğinizi biliyor bashve zsh- ve de çok benzer bir şeyi yapabilir dashve busybox ashburada-belgelerle aynı amaca ulaşmak için (çünkü dashvebusybox burada-do diğer ikisi gibi geçici dosyalar yerine pipetli belgeler) .

Yani, uygulandığında bc...

eval "exec 3<>"<(:) "4<>"<(:)
bc -l <<\INIT <&4 >&3 &
a=1; b=0; c=0;
define a(){ "a="; return (a = c+1); }
define b(){ "b="; return (b = 3*a); }
define c(){ "c="; return (c = s(b)); }
INIT
export BCOUT=3 BCIN=4 BCPID="$!"

... zor kısmı bu. Ve bu eğlenceli kısım ...

set --
until [ "$#" -eq 10 ]
do    printf '%s()\n' b c a >&"$BCIN"
      set "$@" "$(head -n 3 <&"$BCOUT")"
done; printf %s\\n "$@"

... hangi baskı ...

b=3
c=.14112000805986722210
a=1.14112000805986722210
#...24 more lines...
b=3.92307618030433853649
c=-.70433330413228041035
a=.29566669586771958965

... ve hala çalışıyor ...

echo a >&"$BCIN"
read a <&"$BCOUT"
echo "$a"

... bana son değerini aldığı bc'ın ayerine çağırmaktan daha a()bunu ve baskılar artırmak işlevini ...

.29566669586771958965

Aslında onu öldürüp IPC borularını yıkana kadar koşmaya devam edecek ...

kill "$BCPID"; exec 3>&- 4>&-
unset BCPID BCIN BCOUT

1
Çok ilginç. Son bash ve zsh ile dosya tanımlayıcısını belirtmeniz gerekmez, örneğin eval "exec {BCOUT}<>"<(:) "{BCIN}<>"<(:)iyi çalışır
Thor
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.