it-swarm-tr.com

Proses ikamesi ve boru

Aşağıdakileri nasıl anlayacağımı merak ediyordum:

Bir komutun stdout'unu bir başkasının stdinine bağlamak güçlü bir tekniktir. Ancak, birden fazla komutun stdout'unu borulamanız gerekiyorsa ne olur? Proses ikamesi burada devreye girer.

Başka bir deyişle, proses ikamesi borunun yapabildiği her şeyi yapabilir mi?

Proses ikamesi ne yapabilir, ancak boru yapamaz?

89
Tim

Aralarındaki farkı görmenin iyi bir yolu, komut satırında biraz deneme yapmaktır. < karakteri, bir yönlendirme veya kanaldan çok farklı bir şey yapar.

Test için date komutunu kullanalım.

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

Bu anlamsız bir örnektir ancak cat 'ın STDIN üzerinde date çıkışını kabul ettiğini ve tükürdüğünü gösterir. Aynı sonuçlar proses ikamesi ile elde edilebilir:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

Ancak perde arkasında yeni olan şey farklıydı. STDIN akışı vermek yerine, cat aslında açılması ve okunması gereken bir dosyanın adından geçirildi. Bu adımı echo yerine cat kullanarak görebilirsiniz.

$ echo <(date)
/proc/self/fd/11

Cat dosya adını aldığında, dosyanın içeriğini bizim için okur. Öte yandan, echo bize dosyanın geçtiğini gösterdi. Daha fazla değişiklik eklerseniz bu fark daha belirgin hale gelir:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

İşlem ikamesi (bir dosya oluşturur) ve giriş yeniden yönlendirmesini (bir dosyayı STDIN'a bağlayan) birleştirmek mümkündür:

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

Hemen hemen aynı görünüyor ama bu sefer kedi dosya adı yerine STDIN akışından geçirildi. Bunu echo ile deneyerek görebilirsiniz:

$ echo < <(date)
<blank>

Eko STDIN okumadığından ve hiçbir argüman geçmediğinden hiçbir şey elde edemiyoruz.

Borular ve giriş yönlendirmeleri içeriği STDIN akışına aktarır. İşlem ikamesi komutları çalıştırır, çıktılarını özel bir geçici dosyaya kaydeder ve daha sonra bu dosya adını komut yerine geçirir. Hangi komutu kullanırsanız kullanın, dosya adı olarak kabul eder. Oluşturulan dosyanın normal bir dosya değil, artık gerekli olmadığında otomatik olarak kaldırılan adlandırılmış bir kanal olduğunu unutmayın.

140
Caleb

Aksi takdirde imkansız olan işlem ikamesi ile yapabileceğiniz üç şey:.

Birden çok proses girişi

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

Bunu borularla yapmanın bir yolu yok.

STDIN'ın korunması

Aşağıdakilere sahip olduğunuzu varsayalım:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

Ve doğrudan çalıştırmak istiyorsunuz. Aşağıdakiler sefil bir şekilde başarısız olur. Bash zaten betiği okumak için STDIN kullanıyor, bu nedenle diğer girdiler imkansız.

curl -o - http://example.com/script.sh | bash 

Ama bu şekilde mükemmel çalışıyor.

bash <(curl -o - http://example.com/script.sh)

Giden işlem ikamesi

Ayrıca, işlem ikamesinin de başka şekilde çalıştığını unutmayın. Böylece böyle bir şey yapabilirsiniz:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

Bu biraz kıvrık bir örnek ama stdout to /dev/null, piping stderr "İzin reddedildi" hatasının görüntülendiği dosyaların adlarını çıkarmak için bir sed komut dosyasına ve sonra BU sonuçları bir dosyaya gönderir.

İlk komutun ve stdout yönlendirme parantez içinde ( alt kabuk), böylece yalnızca THAT komutunun sonucu /dev/null ve satırın geri kalanıyla karıştırmaz.

26
tylerl

bash veya başka bir gelişmiş Kabuk hakkında konuştuğunuzu varsayalım, çünkü posix Shell süreç ikamesi içermiyor.

bash manuel sayfa raporu:

İşlem Değiştirme
İşlem değiştirme, adlandırılmış yöneltmeleri (FIFO'lar) veya/dev/fd açık dosyaları adlandırma yöntemini destekleyen sistemlerde desteklenir. <(Liste) veya> (liste) biçimini alır. İşlem listesi, girdisi veya çıktısı bir FIFO veya/dev/fd içindeki bir dosyaya bağlı olarak) çalıştırılır. Bu dosyanın adı, > (list) formu kullanılıyorsa, dosyaya yazmak liste için girdi sağlar. <(list) formu kullanılırsa, listenin çıktısını almak için bağımsız değişken olarak iletilen dosyanın okunması gerekir.

Mümkün olduğunda proses ikamesi, parametre ve değişken genişleme, komut ikamesi ve aritmetik genişleme ile eşzamanlı olarak gerçekleştirilir.

Başka bir deyişle ve pratik bir bakış açısından, aşağıdaki gibi bir ifadeyi kullanabilirsiniz

<(commands)

parametre olarak dosya gerektiren diğer komutlar için dosya adı olarak. Veya böyle bir dosya için yeniden yönlendirme kullanabilirsiniz:

while read line; do something; done < <(commands)

Sorunuza geri dönersek, süreç ikamesi ve borularının pek fazla ortak yanı yok gibi görünüyor.

Birden çok komutun çıktısını sırayla oluşturmak istiyorsanız, aşağıdaki formlardan birini kullanabilirsiniz:

(command1; command2) | command3
{ command1; command2; } | command3

ancak işlem değişikliği için yönlendirmeyi de kullanabilirsiniz

command3 < <(command1; command2)

son olarak, eğer command3 bir dosya parametresini kabul eder (stdin yerine)

command3 <(command1; command2)
26
enzotib

Bir komut, dosyaların listesini bağımsız değişken olarak alır ve bu dosyaları girdi olarak (veya çıktı olarak alır, ancak yaygın olarak değil) işlerse, bu dosyaların her biri, işlem değişikliği tarafından şeffaf bir şekilde sağlanan adlandırılmış bir kanal veya/dev/fd sözde dosyası olabilir:

$ sort -m <(command1) <(command2) <(command3)

Bu, sıralama komut dosyasındaki girdi dosyalarının bir listesini alabileceğinden, sıralamak için üç komutun çıktısını "düzenler".

10
camh

İşlem ikamesinin, dosya olarak command çıktısını kullanan <(command) formuyla sınırlı olmadığı belirtilmelidir. Bir dosyayı command giriş olarak besleyen >(command) biçiminde de olabilir. Bu, @ enzotib'in cevabındaki bash kılavuzunun alıntısında da belirtilmiştir.

Yukarıdaki date | cat Örneğinde, aynı etkiyi elde etmek için >(command) biçiminin işlem ikamesini kullanan bir komut,

date > >(cat)

>(cat) öncesi > Gerektiğini unutmayın. Bu, Caleb'in cevabındaki gibi echo tarafından açıkça gösterilebilir.

$ echo >(cat)
/dev/fd/63

Dolayısıyla, fazladan > Olmadan, date >(cat), stderr'a bir ileti yazdıracak olan date /dev/fd/63 İle aynı olur.

Dosya adlarını yalnızca parametre olarak alan ve stdin veya stdout işlemeyen bir programınız olduğunu varsayın. Bunu göstermek için basitleştirilmiş komut dosyasını psub.sh Kullanacağım. psub.sh İçeriği

#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"

Temel olarak, her iki bağımsız değişkeninin de dosya olduğunu (normal dosya olması gerekmez) test eder ve bu durumda, "$1" Satırının ilk alanını awk kullanarak "$2" 'A yazın. Sonra, şimdiye kadar bahsedilenlerin hepsini birleştiren bir komut,

./psub.sh <(printf "a a\nc c\nb b") >(sort)

Bu yazdırılacak

a
b
c

ve eşdeğerdir

printf "a a\nc c\nb b" | awk '{print $1}' | sort

ancak aşağıdakiler işe yaramayacak ve burada süreç ikamesi kullanmalıyız,

printf "a a\nc c\nb b" | ./psub.sh | sort

veya eşdeğer formu

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

./psub.sh Ayrıca yukarıda belirtilenlerin yanında stdin okuyorsa, böyle bir eşdeğer form mevcut değildir ve bu durumda işlem ikamesi yerine kullanabileceğimiz hiçbir şey yoktur (elbette ayrıca adlandırılmış bir kanal veya geçici dosya kullanın, ancak bu başka bir hikaye).

3
Weijun Zhou