Stder'ı nasıl stderr olarak borulayabilirim?


981

Ben bilgileri yazar bir program var stdoutve stderr, ve ihtiyacım grepne geliyor içinden stderr'e hiçe ederken, stdout'u .

Elbette 2 adımda yapabilirim:

command > /dev/null 2> temp.file
grep 'something' temp.file

ama bunu geçici dosyalar olmadan yapmayı tercih ederim. Akıllı boru hileleri var mı?


Benzer bir soru, ancak tutma stdout: unix.stackexchange.com/questions/3514/…
joeytwiddle

Bu soru Bash içindi ama Bourne / Almquist kabuğu için bu ilgili makaleden bahsetmeye değer .
Stephen Niedzielski

10
Böyle bir şey bekliyordum: command 2| othercommand. Bash o kadar mükemmel ki gelişme 1982'de sona erdi, bu yüzden bunu bash'da asla görmeyeceğiz, korkarım.
Rolf

Yanıtlar:


1189

İlk stderr'ı stdout'a yönlendirin - boru; sonra stdout'u şuraya yönlendirin /dev/null(stderr'ın nereye gittiğini değiştirmeden):

command 2>&1 >/dev/null | grep 'something'

Tüm çeşitliliğinde G / Ç yönlendirmesinin ayrıntıları için Bash referans kılavuzundaki Yönlendirmeler bölümüne bakın .

G / Ç yönlendirmelerinin sırasının soldan sağa yorumlandığını, ancak boruların G / Ç yönlendirmelerinin yorumlanmasından önce ayarlandığını unutmayın. 1 ve 2 gibi dosya tanımlayıcıları, açık dosya açıklamalarına referanstır. İşlem 2>&1, dosya tanımlayıcı 2 aka stderr'ın dosya tanımlayıcı 1 aka stdout'un şu anda başvurduğu açık dosya tanımına başvurmasını sağlar (bkz. dup2()Ve open()). İşlem >/dev/nulldaha sonra dosya tanımlayıcı 1'i açık bir dosya tanımına atıfta bulunacak şekilde /dev/nulldeğiştirir, ancak bu dosya tanımlayıcı 2'nin dosya tanımlayıcı 1'in başlangıçta boruyu işaret ettiği açık dosya tanımına başvurduğu gerçeğini değiştirmez.


44
Ben sadece geçen gün / dev / stdout / dev / stderr / dev / stdin tökezledi ve bu aynı şeyi yapmanın iyi yolları olup olmadığını merak ediyordum? Ben her zaman 2> & 1 biraz şaşkın olduğunu düşündüm. Öyle bir şey: command 2> /dev/stdout 1> /dev/null | grep 'something'
Mike Lyons

17
Sen kullanabilirsiniz /dev/stdoutdiğerleri, veya kullanım /dev/fd/N. Kabuk onlara özel durumlar olarak davranmadıkça marjinal olarak daha az verimli olacaklardır; salt sayısal gösterim dosyalara ada göre erişmeyi gerektirmez, ancak aygıtları kullanmak bir dosya adı araması anlamına gelir. Bunu ölçüp ölçemeyeceğiniz tartışmalıdır. Sayısal gösterimin özünden hoşlanıyorum - ama bunu o kadar uzun süredir (çeyrek asırdan fazla; ah!) Kullanıyorum, modern dünyadaki haklarını yargılamak için nitelikli değilim.
Jonathan Leffler

23
@Jonathan Leffler: Düz metin açıklamanızla küçük bir sorun alıyorum 'Stderr'ı stdout'a ve sonra stdout'u / dev / null'a yönlendirin - Birinin sağdan sola yönlendirme zincirlerini okuması gerektiğinden (soldan sağa değil), biz düz metin açıklamamızı da buna uyarlamalıyız: 'Stdout'u / dev / null'a yönlendirin ve sonra stderut'u eskiden olduğu yere yönlendirin' .
Kurt Pfeifle

116
@KurtPfeifle: au contraire! Yönlendirme zincirlerini soldan sağa doğru okumak gerekir, çünkü kabuğun işleyiş şekli budur. İlk işlem 2>&1'stderr'ın şu anda stdout'un gideceği dosya tanımlayıcısına bağlan' anlamına gelir . İkinci işlem, /dev/nullstderr'ı orijinal stdout'a, boruya bırakarak 'stdout'u değiştirir ' olur. Kabuk ilk önce şeyleri boru sembolüne böler, bu nedenle boru yeniden yönlendirmesi 2>&1veya >/dev/nullyeniden yönlendirmelerinden önce gerçekleşir , ancak hepsi bu; diğer işlemler soldan sağa doğrudur. (Sağdan sola çalışmaz.)
Jonathan Leffler

14
Bu konuda beni gerçekten şaşırtan şey, Windows'ta da ( /dev/nullWindows eşdeğeri yeniden adlandırıldıktan sonra nul) çalışmasıdır.
Michael Burr

364

Veya çıktıyı standart hata ve standart çıktı üzerinden değiştirmek için şunu kullanın:

command 3>&1 1>&2 2>&3

Bu yeni bir dosya tanımlayıcı (3) oluşturur ve bunu 1 (standart çıktı) ile aynı yere atar, daha sonra fd 2 (standart hata) ile aynı yere fd 1 (standart çıktı) atar ve son olarak fd 2 (standart hata) atar ) fd 3 ile aynı yere (standart çıkış).

Standart hata artık standart çıktı olarak mevcuttur ve eski standart çıktı standart hatada korunur. Bu aşırı olabilir, ancak umarım Bash dosya tanımlayıcıları hakkında daha fazla ayrıntı verir (her işlem için dokuz tane vardır).


100
Son bir değişiklik 3>&-, stdout'tan oluşturduğunuz yedek tanımlayıcıyı kapatmak olacaktır
Jonathan Leffler

1
Resmin bir dosya vardır tanımlayıcısı oluşturabilir stderrkombinasyonu vardır ve başka stderrve stdout? Başka bir deyişle, stderraynı anda iki farklı dosyaya gidebilir misiniz?
Stuart

Aşağıdakiler hala hataları stdout'a yazdırır. Neyi kaçırıyorum? ls -l not_a_file 3> & 1 1> & 2 2> & 3> error.txt
user48956 27:14

1
@ JonasDahlbæk: Tweak öncelikle bir düzen meselesi. Gerçekten gizemli durumlarda, EOF'u tespit eden ve tespit etmeyen bir süreç arasında fark yaratabilir, ancak bu çok tuhaf durumlar gerektirir.
Jonathan Leffler

1
Dikkat : Bu, FD 3'ün zaten kullanımda olmadığını, kapatmayacağını ve dosya tanımlayıcıları 1 ve 2'nin değiştirilmesini geri almadığını varsayar, bu yüzden bunu başka bir komuta yönlendiremezsiniz. Daha fazla ayrıntı ve geçici çözüm için bu yanıta bakın . {Ba, z} sh için çok daha temiz bir sözdizimi için bu yanıta bakın .
Tom Hale

218

Bash'te, işlem ikamesi kullanarak bir alt kabuğa da yönlendirebilirsiniz :

command > >(stdlog pipe)  2> >(stderr pipe)

Eldeki dava için:

command 2> >(grep 'something') >/dev/null

1
Ekrana çıktı için çok iyi çalışıyor. Grep çıktısını bir dosyaya yeniden yönlendirirsem çözülmemiş içeriğin neden tekrar göründüğüne dair bir fikriniz var mı? Sonra command 2> >(grep 'something' > grep.log)grep.log gelen ungrepped.log aynı aynı çıktıyı içerencommand 2> ungrepped.log
Tim

9
Kullanın 2> >(stderr pipe >&2). Aksi takdirde, "stderr borusu" nun çıkışı "stdlog borusu" ndan geçecektir.
ceving

evet !, 2> >(...)çalışıyor, denedim 2>&1 > >(...)ama olmadı
datdinhquoc

İşte bir dahaki sefere bunu nasıl yapacağımı aradığımda bana yardımcı olabilecek küçük bir örnek. Aşağıdakileri düşünün ... awk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 ) Bu örnekte , konsolumda neyin hata olarak ortaya çıktığını da görmek istedim . Fakat STDOUT çıktı dosyasına gidiyordu. Bu yüzden alt kabuğun içinde, STDOUT'u parantez içindeki STDERR'a yönlendirmeniz gerekir. Çalışırken tee, out-content.txtdosyanın sonunda komuttan STDOUT çıktısı sarılır . Bu benim için tutarsız görünüyor.
will

@datdinhquoc Böyle bir şekilde yaptım2>&1 1> >(dest pipe)
Alireza Mohamadi

195

Bu yanıtların en iyisini birleştirirseniz, şunları yaparsanız:

command 2> >(grep -v something 1>&2)

... sonra tüm stdout stdout olarak korunur ve tüm stderr stderr olarak korunur, ancak stderr'de "bir şey" dizesini içeren hiçbir satır görmezsiniz.

Bu, stdout ve stderr'i tersine çevirmemek veya atmamak, bunları birlikte ezmemek veya geçici dosyalar kullanmamak gibi benzersiz bir avantaja sahiptir.


Is not command 2> >(grep -v something)(olmadan 1>&2) aynı?
Francesc Rosas

11
Hayır, bu olmadan, filtrelenmiş stderr, stdout'a yönlendirilir.
Pinko

1
İhtiyacım olan şey budur - tar çıkışları "dosya okuduğumuz gibi değişti" her zaman bir dizin için, bu yüzden sadece bir satır filtrelemek istiyorum ama başka hatalar olup olmadığını görmek. Yani tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)çalışmalıdır.
Mart'ta

"dizeden başlayarak" demek yanlıştır. Sunulan grep sözdizimi hakkında, yalnızca verilen dize ile başlayan satırları hariç tutacak hiçbir şey yoktur. Çizgiler, belirtilen dizeyi içinde herhangi bir yerde içeriyorlarsa hariç tutulur.
Mike Nakis

@MikeNakis teşekkürler - düzeltildi! (Bu, orijinal cevap
Pinko

102

"Yönlendirmeler" ve "kanallar" ile gerçekten neler olup bittiğini düşünüyorsanız, görselleştirmek çok daha kolaydır. Bash içindeki yönlendirmeler ve kanallar bir şey yapar: 0, 1 ve 2 işlem dosyası tanımlayıcılarının (bkz. / Proc / [pid] / fd / *) işaret ettiği yeri değiştirin.

Ne zaman bir boru veya "|" operatör komut satırında bulunur, gerçekleşecek ilk şey bash'ın bir fifo oluşturması ve sol taraf komutunun FD 1'ini bu fifoya işaret etmesi ve sağ taraf komutunun FD 0'ını aynı fifo'ya göstermesidir.

Daha sonra, her bir taraf için yönlendirme işleçleri soldan sağa değerlendirilir ve tanımlayıcı çoğaltıldığında geçerli ayarlar kullanılır. Bu önemlidir, çünkü boru ilk olarak kurulduğundan, FD1 (sol taraf) ve FD0 (sağ taraf) normalde olduğundan daha önce değiştirilmiştir ve bunların tekrarlanması bu gerçeği yansıtacaktır.

Bu nedenle, aşağıdaki gibi bir şey yazdığınızda:

command 2>&1 >/dev/null | grep 'something'

Sırayla ne olur:

  1. bir boru (fifo) oluşturulur. "FD1 komutu" bu boruya işaret ediyor. "grep FD0" da bu boruya işaret ediyor
  2. "Komut FD2", "Komut FD1" in şu anda işaret ettiği yere işaret edilir (boru)
  3. "FD1 komutu" / dev / null

Böylece, "komut" un FD 2'sine (stderr) yazdığı tüm çıktılar boruya gider ve diğer tarafta "grep" tarafından okunur. "Komut" un FD 1 (stdout) 'a yazdığı tüm çıktılar / dev / null yolunu açar.

Bunun yerine, aşağıdakileri çalıştırın:

command >/dev/null 2>&1 | grep 'something'

İşte olanlar:

  1. bir boru oluşturulur ve "komut FD 1" ve "grep FD 0" işaret edilir.
  2. "FD 1 komutu" / dev / null
  3. "FD 2 komutu", FD 1'in şu anda işaret ettiği yere (/ dev / null) işaret ediyor

Yani, "komut" dan tüm stdout ve stderr / dev / null'a gider. Hiçbir şey boruya gitmez ve böylece "grep" ekranda hiçbir şey göstermeden kapanır.

Ayrıca yeniden yönlendirmelerin (dosya tanımlayıcıları) salt okunur (<), salt okunur (>) veya okuma-yazma (<>) olabileceğini unutmayın.

Son bir not. Bir programın FD1 veya FD2'ye bir şey yazıp yazmadığı tamamen programcıya bağlıdır. İyi programlama uygulaması, hata mesajlarının FD 2'ye ve normal çıkışın FD 1'e gitmesi gerektiğini belirtir, ancak genellikle ikisini karıştıran ya da konvansiyonu görmezden gelen özensiz programlama bulacaksınız.


6
Gerçekten güzel bir cevap. Benim önerim ilk "fifo" kullanımınızı "fifo (adlandırılmış bir boru)" ile değiştirmek olacaktır. Bir süredir Linux kullanıyorum, ancak bir şekilde bunun adı verilen boru için başka bir terim olduğunu öğrenmeyi başaramadım. Bu beni aramaktan kurtaracaktı, ama sonra tekrar bulduğumda gördüğüm diğer şeyleri öğrenemezdim!
Mark Edington

3
@MarkEdington Lütfen FIFO'nun borular ve IPC bağlamında adlandırılmış boru için başka bir terim olduğunu unutmayın . Daha genel bir bağlamda, FIFO, bir sıra veri yapısına ekleme ve çıkarmayı açıklayan First in, first out anlamına gelir.
17'de Loomchild

5
@Loomchild Elbette. Yorumumun amacı, deneyimli bir geliştirici olarak bile, FIFO'nun adlandırılmış boru ile eşanlamlı olarak kullanıldığını hiç görmemiştim . Başka bir deyişle, bunu bilmiyordum: en.wikipedia.org/wiki/FIFO_(computing_and_electronics)#Pipes - Cevapta bana zaman kazandırırdı.
Mark Edington

39

Bash kullanıyorsanız, şunu kullanın:

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines


4
Hayır, stdout ve stderr'i birleştiren |&eşittir 2>&1. Soru açıkça stdout olmadan çıktı istedi .
Profpatsch

3
„'| &' Kullanılırsa, komut1'in standart hatası, komut2'nin boru üzerinden standart girişine bağlanır; kısaca 2> & 1 | ” Bağlantınızdaki dördüncü paragraftan kelimesi kelimesine alınmıştır.
Profpatsch

9
@Profpatsch: Ken'nin cevabı doğrudur, stdout ve stderr'i birleştirmeden önce stdout'u null'a yönlendirdiğine bakın, böylece stdout daha önce / dev / null'a düştüğü için sadece stderr'i kullanacaksınız.
Luciano

3
Ama yine de cevabınızın yanlış olduğunu buldum, >/dev/null |&genişletin >/dev/null 2>&1 | ve stdout inode'un boruya boş olduğu anlamına gelir, çünkü kimse (# 1 # 2 her ikisi de / dev / null inode'a bağlı) stdout inode'una bağlıdır (örneğin ls -R /tmp/* >/dev/null 2>&1 | grep iboş verecektir, ancak ls -R /tmp/* 2>&1 >/dev/null | grep i# Stdout inode bağlanan 2 boru).
Meyve

3
Ken Sharp, test ettim ve ( echo out; echo err >&2 ) >/dev/null |& grep "."çıktı vermiyorum ("err" istediğimiz yerde). man bashdiyor | ederse | & kullanılır ... 2 için kısaltmadır> & 1. Standart hatanın standart çıktıya bu örtülü olarak yönlendirilmesi, komut tarafından belirtilen herhangi bir yeniden yönlendirmeden sonra gerçekleştirilir. İlk olarak komutun FD1'ini null değerine yönlendiririz, sonra komutun FD2'sini FD1'in işaret ettiği yere yönlendiririz, yani. null, bu nedenle grep'in FD0 değeri girilmez. Daha ayrıntılı bir açıklama için stackoverflow.com/a/18342079/69663 adresine bakın .
unhammer

11

Stdout ve stderr dosyalarını kalıcı olarak dosyalara yönlendirmek isteyenler için stderr'de grep yapın, ancak stdout'u bir tty'ye mesaj yazmak için saklayın:

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

6

Bu, command1 stdout'u olduğu gibi bırakırken command1 stderr'yi command2 stdin'e yönlendirir.

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

Alındığı LDP


2

Az önce adlandırılmış yöneltmeler kullanarak stdoutbir komuta ve stderrdiğerine gönderme çözümü buldum .

İşte gidiyor.

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

Daha sonra adlandırılmış boruları çıkarmak muhtemelen iyi bir fikirdir.


0

Rc kabuğunu kullanabilirsiniz .

İlk olarak paketi yükleyin (1 MB'den az).

Eğer standart çıktıyı ve boru standart hatayı atmak nasıl bu bir örnek grep içinde rc:

find /proc/ >[1] /dev/null |[2] grep task

Bash'den ayrılmadan yapabilirsiniz:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Fark etmiş olabileceğiniz gibi, borudan sonra köşeli parantez kullanarak hangi dosya tanımlayıcıyı borulamak istediğinizi belirtebilirsiniz.

Standart dosya tanımlayıcıları aşağıdaki gibi numaralandırılmıştır:

  • 0: Standart giriş
  • 1: Standart çıktı
  • 2: Standart hata

-3

Takip etmeye çalışıyorum, işe yaradığını da buluyorum,

command > /dev/null 2>&1 | grep 'something'

Çalışmıyor. Sadece terminale stderr gönderir. Boruyu yok sayar.
Tripp Kinetics
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.