Stdout'u çoklu komutlara nasıl gönderebilirim?


186

Girdiyi filtreleyen veya harekete geçiren ve ardından çıktı olarak ileten bazı komutlar var, genellikle bence stdout- ama bazı komutlar sadece stdinonunla ne yaparsa yapsın ve hiçbir şey çıkarmaz.

Ben OS X ile en tanıdık ve bunu hemen akla gelen olan iki vardır pbcopyve pbpaste- sistem panoya erişim için gerekmektedir olan.

Her neyse, stdout'u alıp çıktığımı tükürmek stdoutve bir dosyaya gitmek istersem teekomutu kullanabileceğimi biliyorum . Ve hakkında çok az şey biliyorum xargsama aradığım şeyin bu olduğunu sanmıyorum.

stdoutİki (veya daha fazla) komut arasında geçiş yapmak için nasıl bölünebileceğimi bilmek istiyorum . Örneğin:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors

Muhtemelen bundan daha iyi bir örnek var, ancak gerçekten stdout komutunu, onu iletmeyen bir komuta nasıl gönderebileceğimi ve stdout"sessiz" olmaktan saklanırken bilmeyi çok istiyorum - catBir dosyanın nasıl olacağını sormuyorum ve grepbir kısmı ve panoya kopyalayın - belirli komutlar o kadar önemli değil.

Ayrıca - bunu bir dosyaya nasıl göndereceğimi sormuyorum ve stdout- bu "yinelenen" bir soru olabilir (üzgünüm) ama bazılarını aradım ve sadece stdout ile bir dosya arasında nasıl bölüneceği hakkında sorulanları bulabilirim - ve bu soruların cevapları tee, benim için işe yarayacağını sanmıyordum.

Sonunda, "neden sadece pbcopy'yi boru zincirindeki son şey yapmıyorsunuz?" Diye sorabilirsiniz. ve cevabım 1) kullanmak ve hala konsoldaki çıktıyı görmek istersem ne olur? 2) stdoutgirişi yaptıktan sonra çıkmayan iki komutu kullanmak istersem ne olur ?

Oh, ve bir şey daha - kullanabileceğimi teeve adlandırılmış bir pipo ( mkfifo) olduğunun farkındayım , ancak bunun önceden yapılan bir kurulum olmadan satır içi, özlü bir şekilde yapılabileceğini umuyordum :)


Yanıtlar:


239

Bunun teeiçin ikame işlemini kullanabilir ve işleyebilirsiniz:

cat file.txt | tee >(pbcopy) | grep errors

Bu tüm çıktı gönderilir cat file.txtetmek pbcopyve yalnızca sonucunu alırsınız grep. Konsolunuzdaki

Parçaya birden fazla işlem koyabilirsiniz tee:

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors

21
Bir ile endişe pbcopy, ama genel olarak kayda değer: herhangi bir işlemle ikame çıkışları olan , aynı zamanda bir sonraki boru kısmının tarafından görülen, orijinal giriş sonra çözünür; örneğin: seq 3 | tee >(cat -n) | cat -e( cat -ngiriş satırlarını numaralandırır, cat -eyeni satırları işaretler $; bunun cat -ehem orijinal girişe (ilk önce) hem de (sonra) gelen çıkışa uygulandığını görürsünüz cat -n). Birden çok işlem ikamesinden elde edilen çıktı, deterministik olmayan bir düzende gelecektir.
mklement0

49
>(Sadece çalışır bash. Örneğin shbunu kullanarak denerseniz, işe yaramaz. Bu bildirimi yapmak önemlidir.
AAlvz

10
@Alvz: İyi nokta: işlem değiştirme bir POSIX özelliği değildir ; Ubuntu'da dasholduğu gibi hareket eden sh, desteklemiyor ve hatta Bash etkin olduğunda shveya çağrıldığında özelliği bile devre dışı bırakıyor set -o posix. Ancak, süreç değişimlerini destekleyen yalnızca Bash değildir: kshve zshonları da destekleyin (başkalarından emin değil).
mklement0

2
@ mklement0, doğru görünmüyor. Zsh'de (Ubuntu 14.04) satırınız yazdırılıyor: 1 1 2 2 3 3 1 $ 2 $ 3 $ Üzücü çünkü gerçekten de işlevselliğin söylediğiniz gibi olmasını istedim.
Aktau

2
@Aktau: anlatıldığı gibi Nitekim, benim örnek komut sadece çalışmak bashve ksh- zshgörünüşe boru hattı (belki, işte yoluyla çıkış süreci sübstitüsyonlanndan çıktı göndermez tercih sonraki boru hattı segmentine nelerin gönderileceğini kirletmez, çünkü - gerçi hala basar ). Gelen tüm çıkış sipariş öngörülebilir olmayacak şekilde sadece seyrek veya büyük olan yüzeye çıkabilir - sözü kabukları, ancak genellikle süreç sübstitüsyonlanndan düzenli stdout çıkışı ve çıkış karışık olduğu bir tek boru hattı olması iyi bir fikir değil çıktı veri kümeleri.
mklement0

124

Birden fazla dosya adı belirleyebilirsiniz teeve ayrıca standart çıktı bir komuta aktarılabilir. Çıkışı birden fazla komuta göndermek için, birden fazla boru oluşturmanız ve her birini bir çıktı olarak belirtmeniz gerekir tee. Bunu yapmanın birkaç yolu vardır.

İşlem değiştirme

Eğer kabuğunuz ksh93, bash veya zsh ise, işlem değiştirmeyi kullanabilirsiniz. Bu, pipoyu bir dosya adı bekleyen bir komuta iletmenin bir yoludur. Kabuk, boruyu oluşturur /dev/fd/3ve komuta benzer bir dosya adı iletir. Sayı, borunun bağlı olduğu dosya tanıtıcısıdır . Bazı unix değişkenleri desteklemez /dev/fd; bunlarda bunun yerine adlandırılmış bir boru kullanılır (aşağıya bakınız).

tee >(command1) >(command2) | command3

Dosya tanımlayıcıları

Herhangi bir POSIX kabuğunda, açıkça birden çok dosya tanımlayıcısı kullanabilirsiniz . Bu /dev/fd, çıktılarından birinin teedışındaki herkesin adıyla belirtilmesi gerektiğinden , bunu destekleyen bir unix değişkeni gerektirir .

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1

İsimli borular

En temel ve taşınabilir yöntem, adlandırılmış yöneltmeler kullanmaktır . Dezavantajı ise yazılabilir bir dizin bulmanız, boruları oluşturmanız ve daha sonra temizlemeniz gerektiğidir.

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2

10
Beş bash veya belirli bir ksh'ye güvenmek istemeyenler için iki alternatif sürüm sağladığınız için teşekkür ederiz.
trr

tee "$tmp_dir/f1" "$tmp_dir/f2" | command3Mutlaka olmalı command3 | tee "$tmp_dir/f1" "$tmp_dir/f2"sen stdout'unu istediğiniz kadar command3için borulu teehayır? Sürümünüzü altında test ettim dashve teesüresiz olarak girdi beklemesini engelledim, ancak sırayı değiştirmek beklenen sonucu verdi.
Adrian Günter

1
@ AdrianGünter Hayır. Her üç örnek de standart girdiden veri okuyor ve her birine gönderiyor command, command2ve command3.
Gilles,

@ Gilles, niyeti yanlış yorumladım ve pasajı yanlış kullanmaya çalıştım. Açıklama için teşekkürler!
Adrian Günter

Kullanılan kabuk üzerinde kontrolünüz yoksa, açıkça bash kullanabilirsiniz, bunu yapabilirsiniz <command> | bash -c 'tee >(command1) >(command2) | command3'. Benim durumumda yardımcı oldu.
gc5,

16

Sadece işlem değiştirme ile oynayın.

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)

grepmycommand_execsürece özel girdileriyle aynı çıktıya sahip iki ikili dosyadır .


16

Eğer kullanıyorsanız zsh, MULTIOSözellik gücünden yararlanabilirsiniz , yani teekomuttan tamamen kurtulabilirsiniz :

uname >file1 >file2

Sadece çıktısını yazacak unameiki farklı dosyalarına: file1ve file2ne eşdeğerdiruname | tee file1 >file2

Benzer şekilde standart girdilerin yeniden yönlendirilmesi

wc -l <file1 <file2

eşittir cat file1 file2 | wc -l(lütfen bunun aynı olmadığını unutmayın wc -l file1 file2, daha sonra her dosyadaki satır sayısını ayrı olarak sayar).

Elbette MULTIOSçıktıyı dosyalara değil diğer işlemlere yönlendirmek için de kullanabilirsiniz .

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')

3
Bilmek güzel. varsayılan olarak AÇIK MULTIOSolan bir seçenektir (ve kapatılabilir unsetopt MULTIOS).
mklement0

6

Bir komut tarafından üretilen oldukça küçük bir çıktı için, çıktıyı geçici dosyaya yönlendirebilir ve bu geçici dosyayı döngüdeki komutlara gönderebiliriz. Bu, yürütülen komutların sırası önemli olduğunda faydalı olabilir.

Aşağıdaki komut dosyası, örneğin, bunu yapabilir:

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"

Testi ile Ubuntu 16.04 üzerinde çalışan /bin/sholarak dashkabuk:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash

5

Komutu STDOUTbir değişkene alın ve istediğiniz kadar tekrar kullanın:

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy

Yakalamak gerekiyorsa STDERRda, daha sonra kullanmak 2>&1şöyle komuta, sonunda:

commandoutput="$(command-to-run 2>&1)"

3
Değişkenler nerede saklanır? Büyük bir dosyayla ya da onun gibi bir şeyle uğraşıyor olsaydın, bu çok fazla hafıza olmaz mıydı? Değişkenler boyut olarak sınırlı mı?
cwd

1
$ commandoutput mu büyükse ?, boru kullanmak ve ikame işlemi kullanmak daha iyidir.
Nikhil Mulley

4
Açıkçası, bu çözüm yalnızca çıktının boyutunun belleğe kolayca sığacağını bildiğiniz zaman mümkündür ve bir sonraki komutları çalıştırmadan önce tüm çıktının tamponlanmasında sorun olabilirsiniz. Borular, bu iki sorunu isteğe bağlı uzunluk verisine izin vererek ve gerçek zamanlı olarak alıcıya üretildiği gibi aktararak çözer.
trr

2
Küçük çıktınız varsa ve çıktının metin değil, ikili olacağını biliyorsanız bu iyi bir çözümdür. (Kabuk değişkenleri genellikle ikili güvenli değildir)
Rucent88

1
Bunun ikili verilerle çalışmasını sağlayamıyorum. Bence boş baytları veya diğer bazı karakter olmayan verileri yorumlamaya çalışırken yankı ile ilgili bir şey.
Rolf


0

İşte dahil olmak üzere herhangi bir kabuk ile uyumlu, hızlı ve kirli kısmi bir çözüm busybox.

Çözdüğü en dar sorun şudur: Komple stdoutbir konsola yazdırın ve geçici dosyalar veya adlandırılmış yöneltmeler olmadan bir diğerine filtre uygulayın.

  • Aynı ana bilgisayara başka bir oturum başlatın. TTY adını bulmak için, yazın tty. Farz edelim /dev/pty/2.
  • İlk oturumda koş the_program | tee /dev/pty/2 | grep ImportantLog:

Bir tane eksiksiz günlük ve süzülmüş bir tane alırsınız.

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.