Boru ile “tee” kullanırken stderr'ı bir dosyaya nasıl yazarım?


541

Ben nasıl kullanılacağını bilmek teeçıkışını (yazmak için STDOUTbir) aaa.shiçin bbb.outhala Terminalde bunu görüntülenirken,:

./aaa.sh | tee bbb.out

Şimdi görüntülenirken, şimdi STDERRadlı bir dosyaya nasıl yazabilirim ccc.out?


2
Açıklığa kavuşturmak için - stderr'ın ekrana olduğu kadar ekrana da gitmesini ister misiniz?
Charles Duffy

Bunu yaptım, açıklığa kavuşturmak için yazımı düzenleyeceğim. Lhunath'ın çözümünün yeterli olacağına inanıyorum. Yardımlarınız için teşekkürler!
jparanich

Yanıtlar:


784

Terminalde hala STDERR ve STDOUT'u görmek istediğinizi varsayıyorum. Josh tailKelley'in cevabı için gidebilirsin, ama günlük dosyanızı çok hackish ve cludgy çıktısı arka planda tutmak . Bir exra FD tutmanız ve daha sonra onu öldürerek temizleme yapmanız gerektiğine dikkat edin ve teknik olarak bunu a trap '...' EXIT.

Bunu yapmanın daha iyi bir yolu var ve bunu zaten keşfettiniz: tee .

Sadece, stdout için kullanmak yerine, stdout için bir tee ve stderr için bir tane var. Bunu nasıl başaracaksınız? Süreç ikamesi ve dosya yönlendirmesi:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Hadi bölelim ve açıklayalım:

> >(..)

>(...)(işlem ikamesi) bir FIFO oluşturur ve teedinlemenizi sağlar . Ardından, >STDOUT öğesinin commandilkini teedinlediği FIFO'ya yönlendirmek için (dosya yönlendirme) kullanır .

İkincisi için aynı şey:

2> >(tee -a stderr.log >&2)

teeSTDIN'den okunan ve onu içine alan bir işlem yapmak için süreç ikamesini tekrar kullanırız stderr.log. teegirişini tekrar STDOUT'a gönderir, ancak girişi teeSTDERR'ımız olduğu için STDOUT'u tekrar STDERR'ımıza yönlendirmek isteriz. Daha sonra commandSTDERR dosyasını FIFO'nun girişine ( tee'STDIN) yönlendirmek için dosya yeniden yönlendirmesini kullanırız .

Bkz. Http://mywiki.wooledge.org/BashGuide/InputAndOutput

Süreç ikamesi, (POSIX veya Bourne) bashyerine mermi olarak seçme bonusu olarak elde ettiğiniz gerçekten güzel şeylerden biridir sh.


İçinde sh, işleri manuel olarak yapmanız gerekir:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

5
Bunu denedim: $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)işe yarıyor, ama girişi bekliyor. Bunun olmasının basit bir nedeni var mı?
Justin

1
@SillyFreak Ne yapmak istediğinizi veya sorunun ne olduğunu anlamıyorum. yankı testi; exit, stdout'ta herhangi bir çıktı oluşturmaz, bu nedenle hata boş kalır.
lhunath

1
bu yorum için teşekkürler; Daha sonra mantıksal hatamın ne olduğunu anladım: etkileşimli kabuk olarak çağrıldığında bash bir komut istemi yazdırır ve stderr'e çıkışı yankılar. Bununla birlikte, stderr yeniden yönlendirilirse, bash varsayılan olarak etkileşimsiz olarak başlar; karşılaştırmak /bin/bash 2> errve/bin/bash -i 2> err
Aptal ucube

15
Ve "görmek inanmak" için hızlı bir test:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Matthew Wilcoxson

2
Vay canına . İyi cevap: "Hadi
bölelim

672

neden sadece:

./aaa.sh 2>&1 | tee -a log

Bu basitçe yönlendirir stderriçin stdout, tee yankıları böylece hem log ve ekrana etmek. Belki bir şeyleri kaçırıyorum, çünkü diğer çözümlerin bazıları gerçekten karmaşık görünüyor.

Not: bash sürüm 4'ten beri aşağıdakiler için |&bir kısaltma olarak kullanabilirsiniz 2>&1 |:

./aaa.sh |& tee -a log

85
Hem stdout'un (kanal 1) hem de stderr'in (kanal 2) aynı dosyaya (hem stdout hem de sterr karışımını içeren tek bir dosya) kaydedilmesini istiyorsanız, bu iyi çalışır. Diğer, daha karmaşık çözüm, stdout ve stderr'i 2 farklı dosyaya (sırasıyla stdout.log ve stderr.log) ayırmanıza izin verir. Bazen bu önemlidir, bazen değil.
Tyler Rick

19
Diğer çözümler, çoğu durumda gerekenden çok daha karmaşıktır. Bu benim için mükemmel çalışıyor.
dkamins

13
Bu yöntemle ilgili sorun, önemli olabilecek aaa.sh işleminden çıkış / durum kodunu kaybetmenizdir (örn. Bir makefile içinde kullanılırken). Kabul edilen cevapta bu sorun yok.
Stefaan

9
Eğer birleştirilmiş stdout / stderr sakıncası yoksa o zaman ./aaa.sh |& tee aaa.logçalışır (bash).
jfs

5
@Stefaan Komut zincirini ve set -o pipefailsonrasında gelen ;ya &&da yanılmıyorsam çıkış durumunu koruyabileceğinize inanıyorum .
David

59

Bu, bunu google üzerinden bulan kullanıcılar için yararlı olabilir. Sadece denemek istediğiniz örneği açın. Tabii ki, çıktı dosyalarını yeniden adlandırmaktan çekinmeyin.

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}

6
Hayır, sanırım exec bazı açıklamaları kullanabilir. exec >bir dosya tanımlayıcısının hedefini belirli bir hedefe taşımak anlamına gelir. Varsayılan 1'dir, bu nedenle exec > /dev/nullbu oturumda stdout çıktısını şu andan itibaren / dev / null değerine taşır. Bu oturum için geçerli dosya tanımlayıcıları yaparak görülebilir ls -l /dev/fd/. Dene! Sonra exec 2>/tmp/stderr.log., sorun olduğunda ne olacağını görün Ayrıca, exec 3>&13 numaralı yeni bir dosya tanımlayıcı oluşturmak ve dosya tanımlayıcı 1 hedefine yönlendirmek anlamına gelir. Örnekte, hedef komut verildiğinde ekran oldu.
davul

Hem stdout hem de stderr'ı hem ekranda hem de ayrı dosyalarda göstermek için örnek harika !!! Çok teşekkürler!
Marcello de Sales

21

Stderr dosyasını bir dosyaya yeniden yönlendirmek için stdout'u ekrana görüntüleyin ve stdout'u bir dosyaya kaydedin:

./aaa.sh 2> ccc.out | tee ./bbb.out

DÜZENLEME : Stderr ve stdout'u ekrana görüntülemek ve her ikisini de bir dosyaya kaydetmek için bash'ın G / Ç yönlendirmesini kullanabilirsiniz :

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1

1
Ben kullanıcı açıkça ek olarak stderr dosyaya ek olarak kendi konsoluna gitmek istiyor.
Charles Duffy

2
Daha net olmalıydım, ekrana stderr de istedim. Josh Kelley'nin çözümünden hala keyif aldım, ancak lhunath'ın ihtiyaçlarımı daha fazla karşılayacak şekilde buluyorum. Teşekkürler beyler!
jparanich

18

Başka bir deyişle, stdout'u bir filtreye ( tee bbb.out) ve stderr'ı başka bir filtreye ( ) bağlamak istiyorsunuz tee ccc.out. Stdout dışında herhangi bir şeyi başka bir komuta dönüştürmenin standart bir yolu yoktur, ancak dosya tanımlayıcılarını hokkabazlayarak bu sorunu çözebilirsiniz.

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

Ayrıca bkz. Standart hata akışı (stderr) nasıl grep? ve Ne zaman ek bir dosya tanımlayıcı kullanırsınız?

Bash (ve ksh ve zsh) 'de, ancak tire gibi diğer POSIX mermilerinde değil, işlem ikamesini kullanabilirsiniz :

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

Bash'de, komutlar yine de yürütülse ./aaa.shbile , bu komutun biter bitmez geri döndüğüne dikkat edin tee(ksh ve zsh, alt süreçleri bekler). Böyle bir şey yaparsanız bu bir sorun olabilir ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. Bu durumda, dosya tanımlayıcı hokkabazlık veya ksh / zsh kullanın.


4
Bu, stdout / stderr akışlarını olduğu gibi tutmaya izin veren tek cevap gibi görünmektedir (örneğin, onları birleştirmemek). Tatlı!
user1338062

shSüreç ikamesinin mevcut olmadığı cron işleri için yararlı olan en basit yaklaşım .
Roger Dueck

13

Bash kullanıyorsanız:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

Kredi (başımın tepesinden cevap vermemek) buraya: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html


5

Benim durumumda, bir komut dosyası hem stdout hem de stderr'ı bir dosyaya yeniden yönlendirirken komut çalıştırıyordu:

cmd > log 2>&1

Bir hata olduğunda, hata mesajlarına dayalı bazı eylemler gerçekleştirecek şekilde güncellemem gerekiyordu. Tabii ki dup kaldırmak 2>&1ve komut dosyasından stderr yakalamak, ama sonra hata mesajları referans için günlük dosyasına gitmez. @Lhunath'ın kabul edilen cevabının aynı şeyi yapması gerekiyorsa da, yönlendiriyor stdoutve stderrfarklı dosyalara, istediğim şey değil, ancak tam olarak ihtiyacım olan çözümü bulmama yardımcı oldu:

(cmd 2> >(tee /dev/stderr)) > log

Yukarıdaki ile, günlük ikisinin bir kopyasına sahip olacak stdoutve stderrben yakalayabilir stderrhakkında endişe etmeden benim komut dosyasından stdout.


4

Süreç ikamesinin mevcut olmadığı KornShell (ksh) için çalışacaktır,

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

Burada gerçek hüner, bir dizisidir 2>&1 1>&3bizim durumumuzda yönlendirir stderriçin stdoutve yönlendirir stdouttanımlayıcı 3. Bu noktada stderrve stdouthenüz birleştirilmemiştir.

Aslında, stderr(as stdin) teeoturum açtığı yere aktarılır stderr.logve tanımlayıcı 3'e yönlendirilir.

Ve tanımlayıcı 3her zaman günlüğe kaydediyor combined.log. Yani combined.loghem içerir stdoutve stderr.


Şık! Yazık ki güzel bir seçenek çünkü. KSH, btw var :)
runlevel0

2

Zsh kullanıyorsanız , birden fazla yönlendirme kullanabilirsiniz, böylece ihtiyacınız bile olmaz tee:

./cmd 1>&1 2>&2 1>out_file 2>err_file

Burada her bir akışı kendinize ve hedef dosyaya yönlendiriyorsunuz.


Tam örnek

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

Bunun MULTIOSseçeneğin ayarlanmasını gerektirdiğini unutmayın (varsayılan değerdir).

MULTIOS

Birden çok yeniden yönlendirme denenirken örtük tees veya cats gerçekleştirin (bkz. Yeniden Yönlendirme ).

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.