“Yönlendirme” ve “Boru” arasındaki fark nedir?


204

Bu soru biraz aptalca gelebilir, ancak yönlendirme ve borular arasındaki farkı gerçekten göremiyorum.

Yönlendirme, stdout / stdin / stderr komutunu yönlendirmek için kullanılır, örn ls > log.txt.

Borular, bir komutun çıktısını başka bir komuta girdi olarak vermek için kullanılır, örn ls | grep file.txt.

Ama neden aynı şey için iki operatör var?

Neden ls > grepçıktıyı iletmek için yazmıyorsunuz , bu da bir tür yönlendirme değil mi? Neyi özlüyorum?

Yanıtlar:


223

Boru, çıktıyı başka bir programa veya yardımcı programa geçirmek için kullanılır .

Yönlendirme, çıktıyı bir dosyaya veya akışa geçirmek için kullanılır .

Örnek: thing1 > thing2vsthing1 | thing2

thing1 > thing2

  1. Kabuğunuz adlı programı çalıştıracak thing1
  2. Çıktı her şey thing1denilen bir dosyaya yerleştirilecektir thing2. (Not - thing2varsa, üzerine yazılacaktır)

Çıktıyı thing1programdan çağrılan bir programa geçirmek istiyorsanız thing2, aşağıdakileri yapabilirsiniz:

thing1 > temp_file && thing2 < temp_file

hangisi

  1. isimli programı çalıştır thing1
  2. çıktıyı adlı bir dosyaya kaydedin temp_file
  3. thing2klavyedeki kişinin temp_filegirdi olarak yazdığı gibi davranarak adlandırılmış programı çalıştırın .

Ancak, bu clunky, bu yüzden bunu yapmak için daha basit bir yol olarak borular yaptılar. thing1 | thing2aynı şeyi yaparthing1 > temp_file && thing2 < temp_file

Yorumda sorgulanacak daha fazla ayrıntı sağlamak için EDIT:

Eğer >olması hem "programına geçiş" ve "dosyasına yazma" çalıştı, her iki yönde de sorunlara neden olabilir.

İlk örnek: Bir dosyaya yazmaya çalışıyorsunuz. Üzerine yazmak istediğiniz o isimde bir dosya zaten var. Ancak, dosya çalıştırılabilir. Muhtemelen, bu dosyayı çalıştırmayı dener ve girdiyi iletirdi. Çıktıyı yeni bir dosya adına yazmak ve ardından dosyayı yeniden adlandırmak gibi bir şey yapmanız gerekir.

İkinci örnek: Florian Diesch'in belirttiği gibi, ya sistemin başka bir yerinde aynı isimle (bu yürütme yolunda) başka bir komut varsa. Geçerli klasörünüzde bu isimde bir dosya yapmak istemeniz durumunda, sıkışıp kalacaksınız.

Üçüncüsü: eğer bir komutu yanlış yazarsanız, komutun var olmadığı konusunda sizi uyarmaz. Şu anda, yazarsanız size ls | gerp log.txtsöyleyecektir bash: gerp: command not found. Her >ikisini de kastediyorsanız, basitçe sizin için yeni bir dosya oluşturur (daha sonra ne yapacağını bilmediği konusunda uyarın log.txt).


Teşekkür ederim. thing1 > temp_file && thing2 < temp_fileBorularla daha kolay yapmaktan bahsettin . Fakat neden >operatörü bunu yapmak için tekrar kullanmıyorsunuz , örneğin thing1 > thing2komutlar thing1ve thing2? Neden ekstra bir operatör |?
John Threepwood

1
"Çıkışı al ve bir dosyaya yaz", "Çıkışı al ve farklı bir programa geçir" den farklı bir işlemdir. Cevabımı içine daha fazla düşünceleri düzenleyeceğim ...
David Oneill

1
@JohnThreepwood Farklı anlamları var. lessMesela bir şeyi örneğin adlandırılmış bir dosyaya yönlendirmek istersem ? thing | lessve thing > lessfarklı şeyler yaptıkları için tamamen farklılar. Önerdiğiniz şey belirsizlik yaratacaktır.
Darkhogg

"Thing1> temp_file" nin "thing1 | tee temp_file" için sadece sözdizimsel bir şeker olduğunu söylemek doğru mudur? Tee hakkında bilgi edindiğimden beri neredeyse hiç yönlendirmeler kullanmam.
Sridhar Sarnobat

2
@ Sridhar-Sarnobat hayır, teekomut farklı bir şey yapar. teeçıktıyı hem ekrana ( stdout) hem de dosyaya yazar. Yönlendirme sadece dosyayı yapar.
David Oneill

22

Eğer anlamı, yeniden yönlendirmeyi kullanarak çok daha zor ve daha fazla hataya açık hale getirecek foo > barbir komutun bulunup bulunmadığına bağlıysa bar: Her seferinde bir dosyaya yönlendirmek istediğimde, ilk önce hedef dosyam gibi bir komutun olup olmadığını kontrol etmek zorunda kaldım.


Bu, yalnızca env değişkeninizin bir barparçası olan bir dizine yazıyorsanız sorun olabilir $PATH. Eğer / bin gibi bir şey iseniz, o zaman ot bir sorun olabilir. Ancak o zaman bile, barçalıştırılabilir izin kümesinin olması gerekirdi, böylece kabuk yalnızca çalıştırılabilir bir dosyayı bulmak için bardeğil aynı zamanda onu çalıştırabilir. Ve endişe mevcut dosyanın üzerine yazmaksa, nocloberkabuk seçeneğinin yönlendirmelerdeki mevcut dosyaların üzerine yazılmasını önlemesi gerekir.
Sergiy Kolodyazhnyy

13

Unix ve Linux Sistem Yönetimi El Kitabından:

Yönlendirme

Kabuk, <,> ve >> sembollerini bir komutun girişini veya çıkışını bir dosyaya veya dosyadan yeniden yönlendiren talimatlar olarak yorumlar .

borular

Bir komutun STDOUT'unu bir başkasının STDIN'sine bağlamak için | Genelde boru olarak bilinen simge.

Benim yorumum şudur: Komut vermek buysa, bir pipo kullanın. Bir dosyadan veya dosyadan çıktı alıyorsanız yönlendirmeyi kullanın.


12

İki operatör arasında hayati bir fark var:

  1. ls > log.txt -> Bu komut çıktıyı log.txt dosyasına gönderir.

  2. ls | grep file.txt-> Bu komut, ls'nin çıktısını, pipe ( |) işlevini kullanarak grep komutuna gönderir ve grep komutu , önceki komut tarafından sağlanan girişte file.txt dosyasını arar.

İlk senaryoyu kullanarak aynı görevi yapmak zorunda olsaydınız, o zaman olur:

ls > log.txt; grep 'file.txt' log.txt

Böylece |, çıktıyı diğer komuta göndermek için bir boru (ile ) kullanılır, oysa >çıktıyı bir dosyaya yönlendirmek için yeniden yönlendirme (birlikte ) kullanılır.


3

İkisi arasında büyük bir sözdizimsel fark var:

  1. Yönlendirme bir programın argümanıdır
  2. Bir boru iki komutu ayırır

Böyle yönlendirmeler düşünebilirsiniz: cat [<infile] [>outfile]. Bu, emrin önemli olmadığı anlamına gelir: cat <infile >outfileaynıdır cat >outfile <infile. Yönlendirmeleri diğer argümanlarla bile karıştırabilirsiniz: cat >outfile <infile -bve cat <infile -b >outfileher ikisi de mükemmel. Ayrıca birlikte dize birden fazla giriş veya çıkışı (girişleri sıralı okunacak ve tüm çıktı her çıktı dosyasına yazılır) olabilir: cat >outfile1 >outfile2 <infile1 <infile2. Bir yönlendirmenin hedefi veya kaynağı bir dosya adı veya bir akışın adı olabilir (en azından bash'de & 1 gibi).

Ancak, borular bir komutu bir başka komuttan tamamen ayırır, bunları argümanlarla karıştırmazsınız:

[command1] | [command2]

Boru, komut1'den standart çıktıya yazılan her şeyi alır ve onu komut2'nin standart girişine gönderir.

Ayrıca boru ve yönlendirmeyi de birleştirebilirsiniz. Örneğin:

cat <infile >outfile | cat <infile2 >outfile2

İlk catsatırları dosyadan okuyacak, sonra her satırı aynı anda dosyaya yazıp ikinciye gönderecektir cat.

İkincisinde cat, standart girdi önce borudan (içeri doldurma içeriği) okur, daha sonra her bir satırı doldurma2'ye yazarak içeri doldurma2'den okur. Bunu çalıştırdıktan sonra, outfile infile bir kopyası olacak ve outfile2 infile ardından da infile2 içerecektir.

Sonunda, aslında "here string" yönlendirmesini (yalnızca bash ailesi) ve backticks kullanarak örneğinize gerçekten benzer bir şey yaparsınız:

grep blah <<<`ls`

aynı sonucu verecek

ls | grep blah

Ancak, yönlendirme sürümünün ilk önce ls'nin tüm çıktısını bir arabellekte (bellekte) okuyacağını ve sonra o arabelleki aynı anda bir satır tutacak şekilde besleyeceğini düşünüyorum; ve bu çizgiyi grep'e geçirin.


1
Nitpick: Bir fd diğerine yönlendirirseniz sipariş yeniden yönlendirme önemlidir: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blahAyrıca, bir dosyaya yönlendirme sadece son yönlendirme kullanacaktır. echo yes >/tmp/blah >/tmp/blah2sadece yazacak /tmp/blah2.
Ağustos'ta

2
Yönlendirme aslında programın argümanı değildir. Program çıktısının nereye gittiğini (veya girişin nereden geldiğini) bilmeyecek veya önemsemeyecektir. Bu, programı çalıştırmadan önce işleri nasıl düzenleyeceğinizi söylemenin bir yolu.
Alois Mahdal

3

Not: Yanıt, bu mekanizmalar hakkındaki güncel bilgileri, bu site ve unix.stackexchange.com adresindeki akranlar tarafından araştırma ve okumalar hakkında toplanan bilgilerimi yansıtıyor ve zaman geçtikçe güncellenecek. Soru sormaktan veya yorumlarda iyileştirmeler yapmaktan çekinmeyin. Ayrıca, sistemlerin kabuk içinde stracekomutla nasıl çalıştığını görmeyi denemenizi öneririm . Ayrıca, lütfen dahili ya da sistem görüşlerini göz ardı etmeyin; kabuğun işlerin nasıl yürüdüğünü anlamak için bunları bilmek ya da kullanmak zorunda değilsiniz, ancak kesinlikle anlamanıza yardımcı olurlar.

TL; DR

  • |Borular, diskteki bir girişle ilişkilendirilmez, bu nedenle inode disk sayısı yoktur (ancak çekirdek alanındaki pipefs sanal dosya sisteminde inode vardır ), ancak yönlendirmeler genellikle disk girişleri olan ve bu nedenle karşılık gelen dosyaları içerir. inode.
  • Borular lseek()mümkün değildir, bu nedenle komutlar bazı verileri okuyamaz ve sonra geri sarabilir, ancak yönlendirme yaptığınızda >veya <genellikle lseek()nesnenin nesnesi olan bir dosya olduğunda , komutlar istedikleri şekilde gezinebilir.
  • yönlendirmeler, dosya tanımlayıcıları üzerinde yapılan ve birçok olabilir; Borular yalnızca iki dosya tanımlayıcısına sahiptir - biri sol komut için diğeri sağ komut için
  • Standart akışlarda ve borularda yeniden yönlendirme hem tamponlanır.
  • Borular neredeyse her zaman çatallaşmayı içerir ve bu nedenle işlem çiftleri söz konusudur; yönlendirmeleri - her zaman değil, sonuçta her iki durumda da sonuçta ortaya çıkan dosya tanımlayıcıları alt işlemler tarafından miras alınır.
  • Borular her zaman dosya tanımlayıcılarını (bir çift) bağlar, yönlendirmeler - bir yol adı veya dosya tanımlayıcıları kullanır.
  • Borular İşlemler Arası İletişim yöntemidir, yönlendirmeler yalnızca açık dosyalar veya dosyaya benzer nesneler üzerinde yapılan işlemlerdir
  • her ikisi de dup2(), gerçek veri akışının gerçekleştiği dosya tanımlayıcılarının kopyalarını sağlamak için kaputun altında sistemler kullanır .
  • yönlendirmeler ile "global" uygulanabilir execyerleşik komutu (bkz bu ve bu ), bunu yaparsanız bu yüzden exec > output.txther komut için yazacak output.txto andan itibaren. |Borular yalnızca geçerli komut için uygulanır (bu, basit komut veya alt kabuk benzeri seq 5 | (head -n1; head -n2)veya bileşik komutlar anlamına gelir) .
  • Dosyalara yönlendirme yapıldığında, gibi şeyler echo "TEST" > fileve echo "TEST" >> fileher ikisi de open()bu dosya üzerinde syscall kullanır ( ayrıca bakınız ) ve dosya tanımlayıcısını iletmek için bu dosyadan dup2(). Borular |sadece pipe()ve dup2()çağrı kullanır .

  • Komutlar uygulandığında, yöneltmeler ve yeniden yönlendirme, dosya tanımlayıcılarından - kendilerine körce yazabilecekleri veya içsel olarak manipüle edebilecekleri (beklenmeyen davranışlar üretebilecek; aptörneğin stdout'a bile yazamayacakları) dosya tanıtıcılarından başka bir şey değildir. eğer yönlendirme olduğunu biliyorsa).

Giriş

Bu iki mekanizmanın nasıl farklılaştığını anlamak için, temel özelliklerini, ikisinin ardındaki geçmişi ve C programlama dilindeki köklerini anlamak gerekir. Aslında, hangi dosya tanımlayıcılarının ne olduğunu dup2()ve pipe()sistemin nasıl işlediğini ve nasıl çalıştığını bilmek de önemlidir lseek(). Shell, bu mekanizmaları kullanıcıya soyut ettirmenin bir yolu olarak ifade edilir, ancak soyutlamanın daha derine inmesi, kabuğun davranışının gerçek doğasını anlamaya yardımcı olur.

Yönlendirme ve Boruların Kökenleri

Dennis Ritche'nin Prophetic Petroglyphs adlı makalesine göre , borular , Multics işletim sistemi üzerinde çalıştıkları sırada Malcolm Douglas McIlroy tarafından 1964 tarihli bir nottan kaynaklanıyordu . Alıntı:

En güçlü endişelerimi kısaca özetlemek için:

  1. Verilere başka bir şekilde masaj yapılması gerektiğinde, bahçe hortumu - vidası gibi programları bağlamada başka yollar kullanmalıyız. Bu da IO'nun yoludur.

Açıkça görüldüğü gibi, programların o zamanlar diske yazabildikleri, ancak çıktının büyük olması durumunda verimsiz olduğu söylenebilir. Brian Kernighan'ın Unix Pipeline videosundaki açıklamasını alıntı yapmak için :

Öncelikle, büyük bir büyük program yazmak zorunda değilsiniz - zaten işin bir kısmını yapabilecek daha küçük programlarınız var ... Bir başkası, işlediğiniz veri miktarının uygun olmaması durumunda bir dosyada sakladınız ... çünkü hatırlayın, bu şeylerle ilgili disklerin olduğu günlerde geri döndük, eğer şanslıysanız, bir Megabayt ya da iki veri ... .

Böylece kavramsal fark belirgindir: borular, programların birbirleriyle konuşmasını sağlayan bir mekanizmadır. Yönlendirmeler - temel düzeyde dosyaya yazma yoludur. Her iki durumda da, kabuk bu iki şeyi kolaylaştırır, ancak kaputun altında çok şey oluyor.

Daha derine inmek: Sistemler ve kabuğun iç işleyişi

Dosya tanımlayıcı kavramıyla başlıyoruz . Dosya tanımlayıcıları temel olarak bir tam sayı ile temsil edilen açık bir dosyayı (diskte veya bellekte veya anonim dosyada olup olmadığını) tanımlar. İki standart veri akışı (stdin, stdout, stderr) sırasıyla dosya tanımlayıcıları 0,1 ve 2'dir. Onlar nereden geliyor ? Eh, kabuk komutlarında, dosya tanımlayıcıları üst kabuklarından miras alınır. Ve genel olarak tüm işlemler için doğrudur - alt süreç ebeveynin dosya tanımlayıcılarını devralır. İçin cinleri hepsi kalıtsal dosya tanımlayıcıları kapatmak ve / veya başka yerlere yönlendirmek için ortaktır.

Yeniden yönlendirmeye dön. Gerçekte ne Kabuğa komut için dosya tanımlayıcıları hazırlamalarını söyleyen bir mekanizmadır (çünkü komutlar çalıştırılmadan önce kabuk tarafından yapılır) ve kullanıcının önerdiği yere işaret eder. Standart tanımlı çıkış yönlendirme ait

[n]>word

Bu [n]dosya tanıtıcı numarası vardır. Bunu yaptığınızda echo "Something" > /dev/null1 sayısı orada ima edilir ve echo 2> /dev/null.

Kaputun altında, bu dosya tanımlayıcısını dup2()sistem çağrısı yoluyla kopyalayarak yapılır . Hadi alalım df > /dev/null. Kabuk bir çocuk süreç yaratacak dfçalışır, ancak ondan önce de açılacaktır /dev/nulldosya tanıtıcı 3. olarak ve dup2(3,1)dosya tanımlayıcı 3 bir kopyasını yapar verilecektir ve kopya Sen iki dosya var biliyorum 1. olacak file1.txtve file2.txtve ne zaman cp file1.txt file2.txtiki tane aynı dosyaya sahip olacaksın, ama onları bağımsız olarak değiştirebilirsin. Burada da aynı şeyler oluyor. Genellikle, çalıştırmadan önce, daha sonra geri yüklemek amacıyla (ve bu kopya fd # 10 olacaktır) bir kopya dosyası tanımlayıcı # 1 bashyapmak dup(1,10)için yapacağını görebilirsiniz stdout. Önemli olan, yerleşik komutları göz önüne aldığınızda(kabuğun kendisinin bir parçasıdır ve içinde /binveya başka bir yerde dosya yoktur ) veya etkileşimli olmayan kabuklarda basit komutlar , kabuk bir alt süreç oluşturmaz.

Ve sonra [n]>&[m]ve gibi şeyler var [n]&<[m]. Bu, dup2()sadece şu anda olduğu gibi aynı mekanizmanın kullanıcı için uygun olan kabuk sözdizimindeki mekanizmasını kopyalayan dosya tanımlayıcılarıdır .

Yeniden yönlendirme hakkında dikkat edilmesi gereken önemli noktalardan biri, sıralarının sabit olmadığı, ancak kabukta kullanıcının ne istediğini yorumlaması açısından önemlidir. Aşağıdakileri karşılaştırın:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

Bunların kabuk komut dosyalarında pratik kullanımı çok yönlü olabilir:

ve diğerleri.

Sıhhi tesisat pipe()vedup2()

Peki borular nasıl oluşturulur? Via pipe()syscall girdi olarak adlandırılan bir dizi (aka liste) alacak pipefdtürden iki maddeden int(tamsayı). Bu iki tamsayı, dosya tanımlayıcılarıdır. pipefd[0]Borunun okunan sonu olacak ve pipefd[1]yazma sonu olacak. Yani içinde df | grep 'foo', grepbir kopyasını alacaktır pipefd[0]ve dfbir kopyasını alacaktır pipefd[1]. Ama nasıl ? Tabii ki, dup2()çağrı büyüsü ile . For dförneğimizde, diyelim ki pipefd[1]# 4 vardır, bu nedenle kabuk, Çocuk yapmaya yapacak dup2(4,1)(benim hatırlamak cpörnek?) Ve daha sonra yapılacak execve()aslında çalıştırmak için df. Doğal olarak,df# 1 numaralı dosya tanımlayıcısını devralacak, ancak terminalde artık işaret etmediğinin farkında değil, aslında fd # 4, ki bu borunun aslında yazma ucu. Doğal olarak, grep 'foo'farklı dosya tanımlayıcıları dışında da aynı şey gerçekleşecek .

Şimdi, ilginç soru: fd # 2'ye yön veren borular yapabilir miyiz, sadece fd # 1'i değil mi? Evet, aslında |&bash böyle yapar. POSIX standardı, df 2>&1 | grep 'foo'bu amaç için sözdizimini desteklemek için kabuk komut dili gerektirir , ancak aynı zamanda bashbunu |&da yapar .

Unutulmaması gereken nokta, boruların her zaman dosya tanımlayıcıları ile ilgilenmesidir. Diskte bir dosya adı olan ve dosya olarak kullanmanıza izin veren, ancak bir boru gibi davranan, adlandırılmış bir boru var FIFOya da var . Ancak, boru türleri isimsiz boru olarak bilinir - dosya adı yoktur, çünkü bunlar birbirine bağlanan sadece iki nesnedir. Dosyalarla uğraşmadığımız gerçeği de önemli bir sonuçtur: borular mümkün değildir . Bellekteki veya diskteki dosyalar statiktir - programlar bayt 120'ye, ardından bayt 10'a atlamak ve sonra sonuna kadar ileri gitmek için sistem çağrısı kullanabilir . Borular statik değildir - sıralıdır ve bu nedenle onlardan aldığınız verileri geri alamazsınız.|lseek()lseek()lseek(). Bu, bazı programları dosyadan mı yoksa borudan mı okuduklarının farkında olmalarını sağlar ve böylece verimli performans için gerekli ayarlamaları yapabilir; Başka bir deyişle, progeğer yaparsam cat file.txt | progveya algılayabilir prog < input.txt. Bunun gerçek iş örneği kuyruktur .

Boruların diğer iki ilginç özelliği , Linux'ta 4096 byte olan bir arabelleklerinin olması ve aslında Linux kaynak kodunda tanımlandığı şekilde bir dosya sistemine sahip olmaları ! Onlar sadece veri iletmek için bir nesne değiller, kendileri bir veri yapıları! Aslında, hem boruları hem de FIFO'ları yöneten pipefs dosya sistemi bulunduğundan, borular kendi dosya sistemlerinde inode numarasına sahiptir:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Linux'ta borular tek yönlüdür, tıpkı yönlendirme gibi. Unix benzeri bazı uygulamalarda - çift yönlü borular vardır. Her ne kadar kabuk komut dosyası sihri ile , Linux'ta da çift ​​yönlü borular yapabilirsiniz .

Ayrıca bakınız:


2

Diğer cevaplara ek olarak, ince bir anlamsal fark da var; örneğin, yönlendirmelerden daha kolay olan borular:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

İlk örnekte, ilk çağrı headtamamlandığında, boruyu kapatır ve seqsonlandırır, bu nedenle ikincisi için hiçbir giriş yoktur head.

İkinci örnekte, kafa ilk satırı tüketir, ancak kendi stdin borusunu kapattığında , dosya bir sonraki çağrının kullanması için açık kalır.

Üçüncü örnek, readboruyu kapatmamak için kullanırsak, alt işlem içinde hala mevcut olduğunu göstermektedir.

Yani "stream", verileri (stdin vb.) Aktardığımız şeydir ve her iki durumda da aynıdır, ancak boru, iki yönlendirmeden gelen akışları birbirine bağlar; böylece, yeniden yönlendirmenin bir işlemle dosya arasında akışları birleştirdiği Hem benzerliklerin hem de farklılıkların kaynağını görebilir.

PS Eğer benim de bu örnekler hakkında meraklıysanız ve / veya sizi şaşırtıyorsanız trap, işlemlerin nasıl çözüldüğünü görmek için daha fazla kazabilirsiniz.

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Bazen ilk işlem önce kapanır 1, bazen sonra yazdırılır.

exec <&-Borunun davranışını yaklaşık olarak belirlemek için (bir hata ile de olsa) akımı yeniden yönlendirmeden kapatmak için kullanmayı ilginç buldum :

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

“Başa ilk çağrı tamamlandığında boruyu kapatır” Bu aslında iki nedenden dolayı yanlış. Bir, (head -n1; head -n1), her biri tanımlayıcı 0 olarak borunun okunan ucunu miras alan iki komutlu alt kabuktur ve bu nedenle her kabuk alt tanımlayıcı açıktır. İkinci sebep, bunu strace -f bash -c 'seq 5 ile görebilirsiniz. (kafa -n1; kafa -n1) '. Yani ilk kafa sadece dosya tanıtıcısının kopyasını kapatıyor
Sergiy Kolodyazhnyy

Üçüncü örnek de yanlıştır, çünkü readyalnızca ilk satırı tüketir (bu bir bayt 1ve yeni satırdır ). seqtoplam 10 bayt gönderildi (5 sayı ve 5 yeni hat). Böylece, boru ara belleğinde kalan 8 bayt var ve bu yüzden ikinci sonuç headçalışıyor - veriler hala boru ara belleğinde mevcut. BTW, baş sadece 0 byte okunuyorsa çıkar, olduğu gibi çıkarhead /dev/null
Sergiy Kolodyazhnyy

Açıklama için teşekkürler. seq 5 | (head -n1; head -n1)İlk çağrıda boruyu boşalttığını, bu yüzden hala açık durumda olduğunu ancak ikinci çağrı için veri bulunmadığını doğru anlıyor muyum head? Dolayısıyla, boru ile yeniden yönlendirme arasındaki davranış farkı, kafanın tüm verileri borudan çıkarmasından, ancak dosya tanıtıcısının sadece 2 satırını çıkarmasından mı kaynaklanıyor?
Julian de Bhal

Bu doğru. Ve straceilk yorumda verdiğim komutla görülebilecek bir şey . Yeniden yönlendirmeyle, tmp dosyası aranabilir kılan disktedir (çünkü lseek()syscall kullanıyorlar - komutlar dosyanın ilk bayttan sonuncuya kadar istedikleri halde atlayabilirler. Fakat borular sıralı ve aranamazlar. iş ilk önce her şeyi okumak, ya da dosya büyükse - bir kısmını mmap()çağrı yoluyla RAM ile eşleştirin: Bir zamanlar tailPython'da kendim yaptım ve aynı problemle karşılaştım
Sergiy Kolodyazhnyy

Borunun okunan ucunun (dosya tanımlayıcı) önce alt kabuk için verildiğini ve alt (...)kabuk içindeki her komut için kendi stdin'inin kopyasını alacağını hatırlamak da önemlidir (...). Bu yüzden teknik olarak aynı nesneden okunurlar. İlk önce head kendi stdininden okuduğunu düşünüyor. İkincisi head, kendi stdin'in olduğunu düşünüyor. Fakat gerçekte onların fd # 1'i (stdin) sadece borunun sonunu okuyan aynı fd'nin kopyası. Ayrıca, ben bir cevap yolladım, bu yüzden belki bazı şeyleri açıklığa kavuşturmaya yardımcı olur.
Sergiy Kolodyazhnyy

1

Bugün C ile bununla ilgili bir problemim var. Esasen Pipe'ın, yönlendirilse bile, yönlendirmeleri için farklı semantikleri var stdin. Gerçekten ben farklılıklara olursak, borular dışında bir yerde gitmeli stdinböylece stdinve arama sağlar stdpipefarklı şekillerde ele alınabilir (keyfi bir diferansiyeli yapmak).

Bunu düşün. Bir programın diğerine çıkış yaparken bir dosya olduğunu göstermesine rağmen fstatsıfıra döndüğü görülüyor . Bir dosyayı yeniden yönlendirme yaparken bu en azından debian durum (değil , ve vanilya ve ubuntu , vanilya.st_sizels -lha /proc/{PID}/fdwheezystretchjessie14.0416.04

Yeniden cat /proc/{PID}/fd/0yönlendirme yaptıysanız , okumak istediğiniz kadar tekrar etmek mümkün olacaktır. Bunu bir boru ile yaparsanız, görevi art arda ikinci kez çalıştırdığınızda aynı çıktıyı alamayacağınızı fark edeceksiniz.

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.