Bash'te iki boru hattını nasıl ayırabilirsiniz?


143

Nasıl olabilir diff Bash geçici dosyaları kullanmadan iki boru hattının? İki komut boru hattınız olduğunu varsayalım:

foo | bar
baz | quux

Ve diffçıktılarında bulmak istersiniz . Bir çözüm açıkçası:

foo | bar > /tmp/a
baz | quux > /tmp/b
diff /tmp/a /tmp/b

Bash'de geçici dosyalar kullanmadan bunu yapmak mümkün müdür? Fark etmek için boru hatlarından birine borulama yaparak geçici bir dosyadan kurtulabilirsiniz:

foo | bar > /tmp/a
baz | quux | diff /tmp/a -

Ancak her iki boru hattını aynı anda farklı hale getiremezsiniz (en azından bariz bir şekilde değil). /dev/fdGeçici dosyalar kullanmadan bunu yapmak için bazı akıllı numaralar var mı ?

Yanıtlar:


146

2 tmp dosyası içeren bir satır (istediğinizi değil):

 foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

Bash ile deneyebilirsiniz:

 diff <(foo | bar) <(baz | quux)

 foo | bar | diff - <(baz | quux)  # or only use process substitution once

İkinci sürüm , iki numaralı fds yerine,
-- /dev/stdinvs ++ /dev/fd/63veya bir şey göstererek hangi girdinin olduğunu hatırlatacaktır .


Dosya sisteminde adlandırılmış bir boru bile görünmez, en azından bash /dev/fd/63, komutun ayarlayabileceği zaten açık olan bir dosya tanımlayıcısından gerçekten okumak için açılabileceği ve okunabileceği bir dosya adı almak gibi dosya adlarını kullanarak işlem ikamesi uygulayabileceği işletim sistemlerinde görünür. komutunu çalıştırmadan önce yukarı kaldırın. (yani bash pipe(2)çataldan önce kullanır ve daha sonra dup2çıktısından fd 63 quuxiçin bir giriş dosyası tanımlayıcısına yönlendirir. diff)

Bulunan bir sistemde hiçbir "sihirli" /dev/fdya /proc/self/fd, bash işlemi ikame uygulamak için adlandırılmış yöneltme kullanmak olabilir, ama en azından geçici dosyaları aksine onları kendisi yönetmek ve verileriniz dosya sistemine yazılabilir olmaz.

echo <(true)Dosya adını yazdırmak yerine bash'ın işlem ikamesini nasıl uyguladığını kontrol edebilirsiniz . /dev/fd/63Tipik bir Linux sistemine yazdırır . Veya tam olarak hangi sistem çağrılarının kullandığı hakkında daha fazla bilgi için, bir Linux sistemindeki bu komut dosya ve dosya tanımlayıcı sistem çağrılarını izler

strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'

Bash olmadan, adlandırılmış bir boru yapabilirsiniz . Kullan -anlatmak için diffSTDIN'den bir giriş okumak ve diğer olarak adlandırılan boru kullanımı:

mkfifo file1_pipe.txt
foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

Not o yapabilirsiniz sadece boru tek çıkış için birden girdiler tee komutuyla:

ls *.txt | tee /dev/tty txtlist.txt 

Yukarıdaki komut ls * .txt dosyasının terminale çıkışını görüntüler ve txtlist.txt metin dosyasına verir.

Ancak işlem ikamesi ile teeaynı verileri birden fazla boru hattına beslemek için kullanabilirsiniz :

cat *.txt | tee >(foo | bar > result1.txt)  >(baz | quux > result2.txt) | foobar

5
bash olmasa bile, geçici mkfifo a; cmd >a& cmd2|diff a -; rm a
fifoları

Sen args biri için düzenli boru kullanabilirsiniz: pipeline1 | diff -u - <(pipeline2). Daha sonra çıktı , iki numaralı fds yerine, -- /dev/stdinvs ++ /dev/fd/67veya bir şey göstererek hangi girdinin hangisi olduğunu daha net hatırlatacaktır .
Peter Cordes

process substitution ( foo <( pipe )) dosya sistemini değiştirmez. Boru anonimdir ; dosya sisteminde adı yoktur . Kabuk, pipeoluşturmak için sistem çağrısını kullanır , değil mkfifo. Kullan strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'Kendiniz için görmek istiyorsanız izleme dosyası ve dosya tanımlayıcı sistemi çağrılarına. Linux'ta sanal dosya sisteminin bir /dev/fd/63parçasıdır /proc; her dosya tanımlayıcı için otomatik olarak girişleri vardır ve içeriğin bir kopyası değildir. Eğer çağrı yapamazsınız Yani bu bir "geçici dosya" sürece foo 3<bar.txtsayımları
Peter Cordes

@PeterCordes İyi puan. Daha fazla görünürlük için yorumunuzu cevaba ekledim.
VonC

1
@PeterCordes Size herhangi bir düzenleme bırakacağım: Stack Overflow'u ilginç yapan şey: herkes bir cevabı "düzeltebilir".
VonC

127

Bash'da, boru hattını parantez içine alarak komut boru hatlarını ayrı ayrı yürütmek için alt kabukları kullanabilirsiniz. Daha sonra diff'e iletebileceğiniz anonim adlandırılmış kanallar oluşturmak için bunları <ile önek olarak ekleyebilirsiniz.

Örneğin:

diff <(foo | bar) <(baz | quux)

Anonim adlandırılmış kanallar bash tarafından yönetilir, böylece otomatik olarak oluşturulur ve yok edilir (geçici dosyaların aksine).


1
Aynı çözüm - anonim toplu iş - benim redaksiyonumdan çok daha ayrıntılı. +1
VonC

4
Buna Bash'te süreç ikamesi denir .
Franklin Yu

5

Bu sayfaya gelen bazı kişiler, satır satır fark arıyor olabilir, bunun yerine kullanılmalı commveya grep -fkullanılmalıdır.

Belirtilmesi gereken bir şey, tüm cevapların örneklerinde, farkların her iki akış da bitene kadar başlamayacağıdır. Bunu örn. İle test edin:

comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

Bu bir sorunsa, sd (stream diff) işlemini deneyebilirsiniz ; bu comm, yukarıdaki örnekler gibi sıralama ( işlem yaptığı gibi ) veya işlem ikamesi gerektirmez , emir veya büyüklükten daha hızlıdır grep -f ve sonsuz akışları destekler.

Önerdiğim test örneği şu şekilde yazılır sd:

seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'

Ancak seq 100aradaki fark hemen fark edilmemesidir seq 10. Akışlardan biri a tail -fise, fark işlem ikamesi ile yapılamaz.

İşte terminaldeki farklı akışlar hakkında yazdığım bir blog yazısı sd.

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.