Coproc komutunu çeşitli mermilerde nasıl kullanırsınız?


Yanıtlar:


118

ortak işlemler bir kshözelliktir (zaten içinde ksh88). zshsadece sadece eklenmiş ise, başlangıç (90'ların) den özelliğini etti bashiçinde 4.0(2009).

Ancak, davranış ve arayüz 3 kabuklar arasında önemli ölçüde farklıdır.

Yine de, fikir aynı: arka planda bir işe başlamak ve adlandırılmış yöneltmelere başvurmak zorunda kalmadan girişi göndermek ve çıktısını okumak mümkün kılıyor.

Bu, bazı sistemlerde ksh93'ün son sürümlerine sahip çoğu kabuk ve soket çiftine sahip isimsiz borularla yapılır.

In a | cmd | b, averileri besler cmdve bçıktısını okur. Çalıştırma cmdbir ko-işlem olarak kabuk hem olmasını sağlar ave b.

ksh ortak süreçler

İçinde bir işlem kshbaşlat:

cmd |&

cmdAşağıdaki gibi işlemleri yaparak veriyi beslersiniz:

echo test >&p

veya

print -p test

Ve şöyle cmdçıktılarını okuyunuz :

read var <&p

veya

read -p var

cmdHerhangi bir arka plan işi olarak başlatılır , üzerinde , üzerinde kullanabilirsiniz fg, veya yoluyla .bgkill%job-number$!

Borunun yazı ucunu cmdokumaktan okuyup, şunları yapabilirsiniz:

exec 3>&p 3>&-

Ve diğer borunun okuma ucunu kapatmak için (biri cmdyazıyor):

exec 3<&p 3<&-

Boru dosyası tanımlayıcılarını diğer bazı fds'lara kaydetmeden önce ikinci bir ortak işleme başlayamazsınız. Örneğin:

tr a b |&
exec 3>&p 4<&p
tr b c |&
echo aaa >&3
echo bbb >&p

zsh ortak süreçler

İçinde zsheş-süreçler içindekilerle neredeyse aynıdır ksh. Tek gerçek fark, zshortak işlemlerin coprocanahtar kelimeyle başlatılmasıdır .

coproc cmd
echo test >&p
read var <&p
print -p test
read -p var

Yapma:

exec 3>&p

Not: Bu, coprocdosya tanımlayıcısını fd'ye 3(içinde olduğu gibi ksh) taşımaz , ancak çoğaltır. Yani, beslenme veya okuma borusunu kapatmak için hiçbir açık yol var, diğer başlayarak başka coproc .

Örneğin, besleme ucunu kapatmak için:

coproc tr a b
echo aaaa >&p # send some data

exec 4<&p     # preserve the reading end on fd 4
coproc :      # start a new short-lived coproc (runs the null command)

cat <&4       # read the output of the first coproc

Boru bazlı ortak işlemlere ek olarak, zsh(2000 yılında piyasaya sürülen 3.1.6-dev19’dan bu yana) benzeri sahte yapılara sahiptir expect. Çoğu programla etkileşimde bulunmak için, ksh tarzı ortak işlemler işe yaramaz, çünkü programlar bir çıktı olduğunda arabelleğe almaya başlar.

İşte bazı örnekler.

Ortak işleme başla x:

zmodload zsh/zpty
zpty x cmd

(Burada, cmdbasit bir komuttur. Ancak, evalişlevler veya işlevler ile meraklısı şeyler yapabilirsiniz .)

Bir ortak işlem verisi besleyin:

zpty -w x some data

Ortak işlem verilerini okuyun (en basit durumda):

zpty -r x var

Gibi expect, belirli bir kalıpla eşleşen ortak işlemden bir miktar çıktı bekleyebilir.

karma eş süreçleri

Bash sözdizimi çok daha yeni ve yakın zamanda ksh93, bash ve zsh'a eklenen yeni bir özelliğin üzerine inşa edildi. Dinamik olarak tahsis edilmiş dosya tanımlayıcılarının 10'un üzerinde işlenmesine izin vermek için bir sözdizimi sağlar.

bashtemel bir coproc sözdizimi ve genişletilmiş bir metin sunar .

Temel sözdizimi

Bir ortak işleme başlamanın temel sözdizimi şunun gibidir zsh:

coproc cmd

Olarak kshya da zsh, ve ko-işleminden boru ile erişilir >&pve <&p.

Ancak bash, eş işlemden diğer boruya eş işlemden geçen borunun dosya tanımlayıcıları $COPROCdizide döndürülür (sırasıyla ${COPROC[0]}ve ${COPROC[1]}….

Verileri ortak işleme yönlendirin:

echo xxx >&"${COPROC[1]}"

Ortak işlemden verileri okuyun:

read var <&"${COPROC[0]}"

Temel sözdizimi ile o zaman sadece bir ortak işleme başlayabilirsiniz.

Genişletilmiş sözdizimi

Genişletilmiş sözdizimi, sen yapabilirsiniz isim (gibi co-süreçlerini zshzpty eş proccesses):

coproc mycoproc { cmd; }

Komut sahip bir bileşiği, bir komut olarak. (Yukarıdaki örneğin nasıl hatırladığına dikkat edin function f { ...; }.)

Bu kez, dosya tanımlayıcıları vardır ${mycoproc[0]}ve ${mycoproc[1]}.

Bir anda birden fazla eş işlemini başlatabilirsiniz zaman-ama bunu hala (hatta etkileşimli olmayan modda) çalışırken bir eş işlemini başlatmak bir uyarı olsun.

Genişletilmiş sözdizimini kullanırken dosya tanımlayıcılarını kapatabilirsiniz.

coproc tr { tr a b; }
echo aaa >&"${tr[1]}"

exec {tr[1]}>&-

cat <&"${tr[0]}"

Bu şekilde kapatmanın, 4.3'ten önceki bash sürümlerinde çalışmadığını ve bunun yerine yazmak zorunda kaldığınızı unutmayın:

fd=${tr[1]}
exec {fd}>&-

Olduğu gibi kshve zshbu boru dosya tanımlayıcıları yakın-on-exec olarak işaretlenir.

Ama içinde bash, idam komutlara olanlar iletmenin tek yoludur fds bunları çoğaltmak için ise 0, 1ya 2. Bu, tek bir komut için etkileşime girebileceğiniz ortak işlem sayısını sınırlar. (Örnek için aşağıya bakın.)

yash işlemi ve boru hattı yönlendirmesi

yashbaşlı başına bir ortak işlem özelliği yoktur, ancak aynı konsept kendi boru hattı ve işlem yönlendirme özellikleriyle de uygulanabilir. sistem çağrısına yashbir arayüze sahiptir pipe(), bu yüzden bu tür bir şey orada elle kolayca yapılabilir.

Birlikte bir ortak işlem başlatırsınız:

exec 5>>|4 3>(cmd >&5 4<&- 5>&-) 5>&-

İlk önce bir pipe(4,5)5 oluşturur, 5 okuma ucu, 4 okuma ucu), sonra fd 3'ü bir uç boruya diğer uçta stdin ile çalışan bir işleme yönlendirir ve daha önce oluşturulan boruya gider. Ardından, o borunun ihtiyaç duymayacağımız ebeveyndeki yazı ucunu kapatırız. Şimdi kabukta cmd'nin stdinine bağlı fd 3 ve borularla cmd'nin ucuna bağlı fd 4 var.

Uygulamaya yakın bayrağının bu dosya tanımlayıcılarında ayarlanmadığını unutmayın.

Veri beslemek için:

echo data >&3 4<&-

Veri okumak için:

read var <&4 3>&-

Ve fds’ı her zamanki gibi kapatabilirsiniz:

exec 3>&- 4<&-

Şimdi, neden bu kadar popüler değiller?

adlandırılmış yöneltmeler kullanmanın neredeyse hiç yararı yok

Ortak işlemler standart adlandırılmış borularla kolayca uygulanabilir. Tam olarak adlandırılmış boruların ne zaman piyasaya sürüldüğünü bilmiyorum, ancak kshortak işlemlerle ortaya çıktıktan sonra mümkün oldu (muhtemelen 80'lerin ortasında, ksh88 88'de "piyasaya sürüldü", ancak kshbirkaç yıl önce AT & T'de dahili olarak kullanıldığına inanıyorum. Bu) nedenini açıklardı.

cmd |&
echo data >&p
read var <&p

İle yazılabilir:

mkfifo in out

cmd <in >out &
exec 3> in 4< out
echo data >&3
read var <&4

Bunlarla etkileşim kurmak daha kolaydır - özellikle birden fazla ortak işlem yapmanız gerekiyorsa. (Aşağıdaki örneklere bakınız.)

Kullanmanın tek faydası, kullandıktan coprocsonra bu boruları adlandırılmış olanları temizlemek zorunda kalmamanızdır.

kilitlenme eğilimli

Kabuklar birkaç yapıda boru kullanır:

  • Kabuk borular: cmd1 | cmd2 ,
  • komut ikamesi: $(cmd) ,
  • ve işlem ikamesi: <(cmd) , >(cmd).

Bunlarda, veriler farklı işlemler arasında yalnızca bir yönde akar .

Bununla birlikte ortak işlemler ve adlandırılmış yöneltmeler sayesinde, kilitlenme olması kolaydır. Birinin açık kalmasını ve bir işlemi canlı tutmasını önlemek için hangi komutun hangi dosya tanımlayıcısının açık olduğunu takip etmeniz gerekir. Kilitlenmeleri araştırmak zor olabilir, çünkü determinist olmayan bir şekilde ortaya çıkabilirler; örneğin, yalnızca bir boruyu dolduracak kadar veri gönderildiğinde.

expectne için tasarlandığından daha kötü çalışıyor

Ortak işlemlerin temel amacı, kabuğa komutlarla etkileşime girme yolu sağlamaktı. Ancak, çok iyi çalışmıyor.

Yukarıda belirtilen en basit kilitlenme şekli:

tr a b |&
echo a >&p
read var<&p

Çıkışı bir terminale gitmediği için trçıktısını tamponlar. Bu nedenle, dosyanın sonunu görene kadar hiçbir şey çıkarmaz stdinveya çıktısı için veri dolu bir arabellek biriktirir. Yukarıda, kabuğun çıktısını aldıktan sonra a\n(sadece 2 bayt), readsüresiz olarak engelleyecektir çünkü trkabuğun daha fazla veri göndermesini beklemektedir.

Kısacası, borular komutlarla etkileşime geçmek için uygun değildir. Ortak işlemler yalnızca çıktılarını arabelleğe almayan komutlarla veya çıktılarını arabelleğe almadığı söylenebilen komutlarla etkileşimde bulunmak için kullanılabilir ; örneğin, en stdbufson GNU veya FreeBSD sistemlerinde bazı komutları kullanarak .

Bu yüzden expectya da zptybunun yerine sahte terminalleri kullanın. expectkomutlarla etkileşime geçmek için tasarlanmış bir araçtır ve bunu iyi yapar.

Dosya tanımlayıcı kullanımı oldukça basit ve doğru olması zor

Ortak işlemler, basit kabuk boruların izin verdiğinden daha karmaşık bir sıhhi tesisat yapmak için kullanılabilir.

Diğer Unix.SE cevaplarının coproc kullanımına bir örneği vardır.

İşte basitleştirilmiş bir örnek: Bir komutun çıktısının bir kopyasını diğer 3 komuta besleyen ve ardından bu 3 komutun çıktısını birleştiren bir işlev istediğinizi hayal edin.

Tüm borular kullanıyor.

Örneğin: çıktısını beslemek printf '%s\n' foo bariçin tr a b, sed 's/./&&/g've cut -b2-böyle bir şey elde etmek için:

foo
bbr
ffoooo
bbaarr
oo
ar

Öncelikle, bu kesin olarak açık değildir, ancak orada kilitlenme olasılığı vardır ve bu sadece birkaç kilobaytlık veriden sonra gerçekleşmeye başlayacaktır.

Ardından, kabuğunuza bağlı olarak, farklı şekilde ele alınması gereken birçok farklı sorunla karşılaşacaksınız.

Örneğin, zshşununla:

f() (
  coproc tr a b
  exec {o1}<&p {i1}>&p
  coproc sed 's/./&&/g' {i1}>&- {o1}<&-
  exec {o2}<&p {i2}>&p
  coproc cut -c2- {i1}>&- {o1}<&- {i2}>&- {o2}<&-
  tee /dev/fd/$i1 /dev/fd/$i2 >&p {o1}<&- {o2}<&- &
  exec cat /dev/fd/$o1 /dev/fd/$o2 - <&p {i1}>&- {i2}>&-
)
printf '%s\n' foo bar | f

Yukarıda, ortak işlem fds idamda yakın bayrak ayarlanmış, ancak onlardan kopyalananlar (içinde olduğu gibi ) bulunmuyor{o1}<&p . Bu yüzden, kilitlenmeleri önlemek için, kendilerine ihtiyaç duymayan herhangi bir işlemde kapalı olduklarından emin olmalısınız.

Benzer şekilde, exec catbir boruyu açık tutma konusunda yalan söyleyen hiçbir kabuk işlemi olmadığından emin olmak için alt kabuk kullanmalı ve sonunda kullanmalıyız .

İle ksh(burada ksh93) o olması gerekir:

f() (
  tr a b |&
  exec {o1}<&p {i1}>&p
  sed 's/./&&/g' |&
  exec {o2}<&p {i2}>&p
  cut -c2- |&
  exec {o3}<&p {i3}>&p
  eval 'tee "/dev/fd/$i1" "/dev/fd/$i2"' >&"$i3" {i1}>&"$i1" {i2}>&"$i2" &
  eval 'exec cat "/dev/fd/$o1" "/dev/fd/$o2" -' <&"$o3" {o1}<&"$o1" {o2}<&"$o2"
)
printf '%s\n' foo bar | f

( Not: Bunun yerine kshkullanan ve Linux'taki gibi çalışan sistemler üzerinde çalışmaz .)socketpairspipes/dev/fd/n

'De ksh, fds 2, komut satırında açıkça geçilmediği sürece, çalıştırılmaya hazır bayrağı ile işaretlenmiştir. Birlikte gibi kullanılmayan dosya tanımlayıcıları kapatmak gerekmez yüzden zsh-ama yapmamız gereken neden de var {i1}>&$i1ve kullanımı evalbu yeni değeri için $i1geçirilecek, teeve cat...

Bu bashyapılamaz, çünkü uygulayıcıya yakın bayrağını engelleyemezsiniz.

Yukarıda, nispeten basit çünkü sadece basit harici komutları kullanıyoruz. Orada kabuk yapıları kullanmak istediğinizde daha karmaşık hale gelir ve kabuk hatalarına koşmaya başlarsınız.

Yukarıdakileri adlandırılmış yöneltmeler kullanarak karşılaştırın:

f() {
  mkfifo p{i,o}{1,2,3}
  tr a b < pi1 > po1 &
  sed 's/./&&/g' < pi2 > po2 &
  cut -c2- < pi3 > po3 &

  tee pi{1,2} > pi3 &
  cat po{1,2,3}
  rm -f p{i,o}{1,2,3}
}
printf '%s\n' foo bar | f

Sonuç

Bir komutla etkileşime girmek istiyorsanız expect,, veya zsh's zptyveya adlandırılmış yöneltmeler kullanın.

Borularla süslü bir sıhhi tesisat yapmak istiyorsanız, adlandırılmış borular kullanın.

Ortak süreçler yukarıdakilerin bazılarını yapabilir, ancak önemsiz olmayan herhangi bir şey için ciddi baş kaşıma yapmaya hazır olun.


Gerçekten harika bir cevap. Onu düzelttirmek zaman özellikle biliyorum ama yok en azından bash 4.3.11, sen can şimdi kapatılacak coproc dosya tanımlayıcıları doğrudan bir aux ihtiyaç duymadan. değişken; Cevabınız örneğin açısından exec {tr[1]}<&- artık çalışacağını (coproc en stdin kapatmak için; kodunuzu (dolaylı olarak) kapatmaya çalıştığını unutmayın {tr[1]}kullanarak >&-, ama {tr[1]}coproc en olduğunu Stdin ve birlikte kapalı olmalıdır <&-). Düzeltme 4.2.25, hala sorunu gösteren ve olmayan arasında bir yerlere gelmiş olmalı 4.3.11.
mklement0

1
@ mklement0, teşekkürler. exec {tr[1]}>&-gerçekten de yeni sürümlerle çalışıyor gibi görünüyor ve CWRU / changelog girişinde referans alıyor ( {array [ind]} gibi kelimelerin geçerli yönlendirme olmasına izin ver ... 2012-09-01). exec {tr[1]}<&-(ya da >&-sadece close()her ikisini de çektiği için bir fark yaratmamasına rağmen daha doğru bir eşdeğer ) coproc'un stdin'ini kapatmıyor, ama borunun o coproc'a yazdığı son.
Stéphane Chazelas

1
@ mklement0, iyi nokta, ben güncelledik ve ekledim yash.
Stéphane Chazelas

1
Bunun bir avantajı mkfifo, yarış koşulları ve boru erişiminde güvenlik konusunda endişelenmenize gerek olmamasıdır. Yine de fifo ile çıkma konusunda endişelenmen gerekiyor.
Otheus

1
Kilitlenmeler hakkında: stdbufkomut, en azından bazılarının önlenmesine yardımcı olabilir. Linux ve bash altında kullandım. Her neyse, @ StéphaneChazelas'ın doğru olduğuna inanıyorum: “kafa kaşıma” aşaması benim için sadece adlandırılmış borulara geçtiğimde sona erdi.
30'da shub

7

Ortak işlemler ilk önce ksh88kabuk (1988) ile bir kabuk kodlama dilinde ve daha sonra zsh1993'ten önce bir noktada tanıtıldı .

Ksh altında ortak bir işlem başlatmak için kullanılan sözdizimi command |&. Buradan başlayarak commandstandart girişe yazabilir print -pve onun standart çıkışını okuyabilirsiniz read -p.

Birkaç on yıldan fazla bir süre sonra, bu özellikten yoksun olan bash, sonunda 4.0 sürümünde tanıtmıştı. Ne yazık ki, uyumsuz ve daha karmaşık bir sözdizimi seçildi.

Bash 4.0 ve daha yenisi altında, aşağıdaki coprockomutu içeren bir ortak işlemi başlatabilirsiniz , örneğin:

$ coproc awk '{print $2;fflush();}'

Ardından stdin komutuna bir şey iletebilirsiniz:

$ echo one two three >&${COPROC[1]}

ve awk çıktısını şu şekilde okuyun:

$ read -ru ${COPROC[0]} foo
$ echo $foo
two

Ksh altında, bu olurdu:

$ awk '{print $2;fflush();}' |&
$ print -p "one two three"
$ read -p foo
$ echo $foo
two

-1

"Coproc" nedir?

Kabuk ile işbirliği yapan ikinci bir işlem anlamına gelen “eş işlem” için kısadır. Komutun sonunda bir "&" ile başlayan bir arka plan işine çok benzer, bunun haricinde, ana kabuğuyla aynı standart giriş ve çıkışı paylaşmak yerine, standart G / Ç özel bir tarafından ana kabuğa bağlanır. FIFO denilen boru türü. Referans için buraya tıklayın

Biri zsh ile bir coproc başlatır

coproc command

Komut, stdin'den ve / veya stdout'a yazmaya ya da coproc olarak kullanılmaya çok hazırlıklı olmalı.

Bu makaleyi buradan okuyun exec ve coproc arasında bir örnek çalışma sunar


Cevabınıza makalenin bir kısmını ekler misiniz? Temsil edildiğinden beri bu konuyu U&L'de ele almaya çalışıyordum. Cevabınız için teşekkürler! Ayrıca etiketi zsh yerine Bash olarak ayarladım.
slm

@slm Sen zaten Bash korsanlarına işaret ettin. Orada yeterince örnek gördüm. Niyetiniz bu soruyu dikkatle ortaya çıkarmaksa, evet başardınız:>
Valentin Bajrami

Özel tip borular değiller, eskiden kullanılanlarla aynı borular |. (çoğu mermide boru, ksh93 de soket çifti kullanmaktır). Borular ve priz çiftleri ilk giren ilk çıkar, hepsi FIFO'dur. mkfifoadlandırılmış yöneltmeler yapar, işlemciler adlandırılmış yöneltmeler kullanmaz.
Stéphane Chazelas

@slm zsh için üzgünüm ... aslında ben zsh üzerinde çalışıyorum. Bazen akışla yapma eğilimindeyim. Bash'de de iyi çalışıyor ...
Munai Das Udasin

@ Stephane Chazelas Bir yerde I / O'nun FIFO adı verilen özel borularla bağlantılı olduğunu bir yerde okuduğumdan eminim ...
Munai Das Udasin

-1

İşte başka bir iyi (ve çalışan) örnek - BASH ile yazılmış basit bir sunucu. Lütfen OpenBSD'lere ihtiyacınız olacağını unutmayın netcat, klasik olan işe yaramaz. Elbette, unix one yerine inet soketini kullanabilirsiniz.

server.sh:

#!/usr/bin/env bash

SOCKET=server.sock
PIDFILE=server.pid

(
    exec </dev/null
    exec >/dev/null
    exec 2>/dev/null
    coproc SERVER {
        exec nc -l -k -U $SOCKET
    }
    echo $SERVER_PID > $PIDFILE
    {
        while read ; do
            echo "pong $REPLY"
        done
    } <&${SERVER[0]} >&${SERVER[1]}
    rm -f $PIDFILE
    rm -f $SOCKET
) &
disown $!

client.sh:

#!/usr/bin/env bash

SOCKET=server.sock

coproc CLIENT {
    exec nc -U $SOCKET
}

{
    echo "$@"
    read
} <&${CLIENT[0]} >&${CLIENT[1]}

echo $REPLY

Kullanımı:

$ ./server.sh
$ ./client.sh ping
pong ping
$ ./client.sh 12345
pong 12345
$ kill $(cat server.pid)
$
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.