Yanıtlar:
ortak işlemler bir ksh
özelliktir (zaten içinde ksh88
). zsh
sadece sadece eklenmiş ise, başlangıç (90'ların) den özelliğini etti bash
iç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
, a
verileri besler cmd
ve b
çıktısını okur. Çalıştırma cmd
bir ko-işlem olarak kabuk hem olmasını sağlar a
ve b
.
İçinde bir işlem ksh
başlat:
cmd |&
cmd
Aş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
cmd
Herhangi bir arka plan işi olarak başlatılır , üzerinde , üzerinde kullanabilirsiniz fg
, veya yoluyla .bg
kill
%job-number
$!
Borunun yazı ucunu cmd
okumaktan okuyup, şunları yapabilirsiniz:
exec 3>&p 3>&-
Ve diğer borunun okuma ucunu kapatmak için (biri cmd
yazı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
İçinde zsh
eş-süreçler içindekilerle neredeyse aynıdır ksh
. Tek gerçek fark, zsh
ortak işlemlerin coproc
anahtar 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, coproc
dosya 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, cmd
basit bir komuttur. Ancak, eval
iş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.
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.
bash
temel bir coproc
sözdizimi ve genişletilmiş bir metin sunar .
Bir ortak işleme başlamanın temel sözdizimi şunun gibidir zsh
:
coproc cmd
Olarak ksh
ya da zsh
, ve ko-işleminden boru ile erişilir >&p
ve <&p
.
Ancak bash
, eş işlemden diğer boruya eş işlemden geçen borunun dosya tanımlayıcıları $COPROC
dizide 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, sen yapabilirsiniz isim (gibi co-süreçlerini zsh
zpty 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 ksh
ve zsh
bu 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
, 1
ya 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
baş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 yash
bir 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<&-
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 ksh
ortak işlemlerle ortaya çıktıktan sonra mümkün oldu (muhtemelen 80'lerin ortasında, ksh88 88'de "piyasaya sürüldü", ancak ksh
birkaç 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 coproc
sonra bu boruları adlandırılmış olanları temizlemek zorunda kalmamanızdır.
Kabuklar birkaç yapıda boru kullanır:
cmd1 | cmd2
,$(cmd)
,<(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.
expect
ne için tasarlandığından daha kötü çalışıyorOrtak 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 stdin
veya çıktısı için veri dolu bir arabellek biriktirir. Yukarıda, kabuğun çıktısını aldıktan sonra a\n
(sadece 2 bayt), read
süresiz olarak engelleyecektir çünkü tr
kabuğ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 stdbuf
son GNU veya FreeBSD sistemlerinde bazı komutları kullanarak .
Bu yüzden expect
ya da zpty
bunun yerine sahte terminalleri kullanın. expect
komutlarla etkileşime geçmek için tasarlanmış bir araçtır ve bunu iyi yapar.
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 bar
iç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 cat
bir 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 ksh
kullanan ve Linux'taki gibi çalışan sistemler üzerinde çalışmaz .)socketpairs
pipes
/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}>&$i1
ve kullanımı eval
bu yeni değeri için $i1
geçirilecek, tee
ve cat
...
Bu bash
yapı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
Bir komutla etkileşime girmek istiyorsanız expect
,, veya zsh
's zpty
veya 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.
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.
yash
.
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.
stdbuf
komut, 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.
Ortak işlemler ilk önce ksh88
kabuk (1988) ile bir kabuk kodlama dilinde ve daha sonra zsh
1993'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 command
standart girişe yazabilir print -p
ve 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 coproc
komutu 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
"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
|
. (çoğu mermide boru, ksh93 de soket çifti kullanmaktır). Borular ve priz çiftleri ilk giren ilk çıkar, hepsi FIFO'dur. mkfifo
adlandırılmış yöneltmeler yapar, işlemciler adlandırılmış yöneltmeler kullanmaz.
İş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)
$
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ındanexec {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üzeltme4.2.25
, hala sorunu gösteren ve olmayan arasında bir yerlere gelmiş olmalı4.3.11
.