Proses ikamesi ve boru


86

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

Bir komutun stdout'unu diğerinin stdinine sokmak güçlü bir tekniktir. Ancak, çoklu komutların stdout'unu aktarmanız gerekirse? Proses ikamesi burada devreye giriyor.

Başka bir deyişle, işlem ikame işlemi borunun yapabileceği her şeyi yapabilir mi?

İşlem ikamesi ne yapabilir, fakat boru olamaz?

Yanıtlar:


134

Aralarındaki farkı kırmanın iyi bir yolu, komut satırında biraz deneme yapmaktır. <Karakterin kullanımındaki görsel benzerliğe rağmen, bir yönlendirme veya borudan çok farklı bir şey yapar.

dateTest etmek için komutu kullanalım .

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

Bu anlamsız bir örnektir, ancak STDIN'in catçıktısını kabul ettiğini dateve tükürdüğünü göstermektedir. Proses ikamesiyle aynı sonuçlar elde edilebilir:

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

Ancak sahnelerin ardında ne olduğu farklıydı. Bir STDIN akışı verilmesi yerine cat, aslında açılıp okunması gereken bir dosyanın adı geçildi. Bu adımı echoyerine kullanarak görebilirsiniz cat.

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

Cat dosya adını aldığında, dosyanın içeriğini bizim için okur. Öte yandan, yankı az önce 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 değiştirme (bir dosya üreten) ve giriş yönlendirmesini (bir dosyayı STDIN'e bağlayan) birleştirmek mümkündür:

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

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

$ echo < <(date)
<blank>

Yankı STDIN'i okumadığı ve hiçbir argüman geçmediği için hiçbir şey alamadık.

Borular ve giriş yönlendirmeleri içeriği STDIN akışına sürükler. İşlem değiştirme komutları çalıştırır, çıktılarını özel bir geçici dosyaya kaydeder ve ardından bu dosya adını komut yerine geçirir. Hangi komutu kullanıyorsanız kullanın, onu bir dosya adı olarak kabul eder. Oluşturulan dosyanın normal bir dosya olmadığını, artık gerekmediğinde otomatik olarak kaldırılan adlandırılmış bir kanal olduğunu unutmayın.


Cesaretle anladıysam, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 , işlem değiştirmenin, adlandırılmış yöneltmeler yerine geçici dosyalar oluşturduğunu söylüyor. Bildiğim kadarıyla geçici dosyalar oluşturma. Pipete yazmak asla diske yazmayı
Adobe

Bu cevabın okunaklı olduğunu biliyorum çünkü grok kelimesini kullanıyor : D
aqn

2
@Adbebe, geçici dosya işlem ikamesinin üretip üretmediğini, ile adlandırılmış bir kanal olup olmadığını onaylayabilirsiniz [[ -p <(date) ]] && echo true. Bu, truebash 4.4 veya 3.2 ile çalıştırdığımda ortaya çıkıyor.
De Novo

24

bashPosix kabuğunun işlem değiştirmesi olmadığından, hakkında veya başka bir gelişmiş kabuktan bahsettiğinizi düşünmeliyim .

bash manuel sayfa raporları:

İşlem Değiştirme
İşlem değiştirme, adlandırılmış boruları (FIFO) destekleyen sistemlerde veya açık dosyaları adlandırmada / dev / fd yönteminde desteklenir. <(List) veya> (list) şeklini alır. İşlem listesi bir FIFO'ya bağlı giriş veya çıkışla veya / dev / fd içindeki bir dosyayla çalıştırılır. Bu dosyanın adı, genişlemenin sonucu olarak o anki komuta argüman olarak iletilir. > (Liste) formu kullanılırsa, dosyaya yazmak liste için girdi sağlayacaktır. <(List) formu kullanılırsa, argüman olarak iletilen dosya listenin çıktısını almak için okunmalıdır.

Mümkün olduğunda, işlem değiştirme, parametre ve değişken genişleme, komut değiştirme ve aritmetik genişleme ile aynı anda gerçekleştirilir.

Başka bir deyişle ve pratik açıdan aşağıdaki gibi bir ifade kullanabilirsiniz.

<(commands)

parametre olarak bir 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, bana göre işlem ikame ve borularının pek ortak bir yanı yok.

Birden fazla komutun çıktısını sıraya dizmek istiyorsanız, aşağıdaki formlardan birini kullanabilirsiniz:

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

ancak işlem ikameinde yeniden yönlendirme kullanabilirsiniz.

command3 < <(command1; command2)

Son olarak, eğer command3bir dosya parametresini kabul ederse (stdin yerine)

command3 <(command1; command2)

yani <() ve <<() aynı etkiyi yapar, değil mi?
Solfish

@solfish: tam değil: firse, bir dosya isminin beklendiği yerde kullanılabilir, ikincisi, bu dosya adı için bir giriş yönlendirmesidir
enzotib, 24:17

23

İşlem değiştirme ile yapabileceğiniz üç şey var; aksi halde imkansız.

Birden fazla işlem girişi

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

Bunu borularla yapmanın bir yolu yok.

STDIN’i Koruma

Aşağıdakilere sahip olduğunuzu söyleyin:

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

Ve doğrudan çalıştırmak istiyorsun. Aşağıdaki sefil başarısız olur. Bash zaten betiği okumak için STDIN kullanıyor, bu nedenle başka girdi imkansız.

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

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

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

Giden işlem değiştirme

Ayrıca, işlem değişikliğinin başka şekilde de işe yaradığı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 bir dolambaçlı örnek biraz, ama gönderir stdout'u için /dev/nullboru ederken, stderr'yi bir sed komut dosyasına bir hata görüntülenir ve sonra bir dosyaya BU sonuçları gönderir edildi "İzin verilmedi" hangi dosyaların adlarını ayıklamak için.

İlk komut ve stdout yeniden yönlendirmenin parantez içerisinde ( alt kabuk ) olduğunu ve yalnızca THAT komutunun sonucunun gönderileceğini /dev/nullve satırın geri kalanıyla karışmadığını unutmayın.


O içinde dikkati çekiyor diffÖrneğin sen durumda umurumda isteyebilirsiniz cdbaşarısız olabilir: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
phk

"stderr piping yaparken": bunun piping olmadığı , ancak bir fifo dosyasından geçtiği nokta değil mi?
Gauthier

@Gauthier no; komut, bir fifo ile değil, dosya tanıtıcısına referansla değiştirilir. Bu yüzden "echo <(echo)", FD 63 numaralı numaradan okuyan veya yazan özel bir karakter cihazı olan "/ dev / fd / 63" gibi bir şey vermelidir.
tylerl

10

Bir komut argüman olarak dosyaların bir listesini alırsa ve bu dosyaları giriş (veya çıkış olarak işler, ancak genel olarak değil) işlerse, bu dosyaların her biri işlem değişikliği ile şeffaf bir şekilde sağlanan adlandırılmış bir boru veya / dev / fd sözde dosya olabilir:

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

Bu, üç komutun çıktısını "düzenleyecektir", çünkü sıralama komut satırındaki girdi dosyalarının bir listesini alabilir.


1
IIRC <(command) sözdizimi yalnızca bash özelliğidir.
Philomath

@ Philomath: O da ZSH'de.
Caleb,

ZSH her şeye sahiptir ... (ya da en azından dener).
Philomath

@Philomath: İşlem değişikliği diğer kabuklarda nasıl uygulanır?
camh

4
@ Philomath <(), birçok gelişmiş kabuk özelliği gibi, aslında bir ksh özelliğidir ve bash ve zsh tarafından benimsenmiştir. psubözellikle bir balık özelliği, POSIX ile ilgisi yoktur.
Gilles

3

İşlem değiştirmenin bir dosya <(command)çıktısı kullanan formla sınırlı olmadığı belirtilmelidir command. >(command)Girdi olarak bir dosyayı besleyen biçimde commandde olabilir. Bu ayrıca @ enzotib'in cevabında bash el kitabının alıntılarında da belirtilmiştir.

İçin date | cat, yukarıdaki örnekte, biçim proses ikame kullanan bir komut >(command)aynı etki olacaktır elde etmek için,

date > >(cat)

Not >önce >(cat)gereklidir. Bu yine echo@ Caleb'in cevabındaki gibi açıkça gösterilebilir .

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

Yani, ekstra olmadan >, date >(cat)aynı olurdu date /dev/fd/63stderr'e bir ileti basar hangi.

Yalnızca parametre olarak dosya adlarını alır ve işlemez bir program olduğunu varsayalım stdinveya stdout. Bunu psub.shgöstermek için aşırı basitleştirilmiş betiği kullanacağım. İçeriği psub.shDİR

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

Temel olarak, her iki argümanının da dosyalar olduğunu test eder (mutlaka normal dosyalar değil) ve bu durumda, awk kullanmak "$1"için her satırın ilk alanını yazın "$2". O zaman, şu ana kadar bahsedilenleri birleştiren bir komut,

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

Bu yazdıracak

a
b
c

ve eşittir

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

fakat aşağıdakiler işe yaramayacak ve burada işlem değişikliğini kullanmak zorundayı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

Eğer ./psub.shaynı zamanda okur stdinyukarıda ifade edilenlerden yanında, o zaman böyle bir eşdeğer formu yok ve hiçbir şey olduğu durumda biz tabii ki aynı zamanda bir adlandırılmış kanal veya geçici dosya kullanabilirsiniz (süreç ikamesi yerine kullanabileceğiniz, ama bu başka bulunuyor hikaye).

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.