Ekranda sadece stderr'i göster, ancak stdout ve stderr'yi dosyaya yaz


24

Bunu başarmak için BASH büyüsünü nasıl kullanabilirim?
Sadece stderr çıktısını ekranda görmek istiyorum,
ancak hem stdout hem de stderr'nin bir dosyaya yazılmasını istiyorum.

Açıklama: Hem stdout hem de stderr'ın aynı dosyada kalmasını istiyorum. Oldukları sırayla.
Maalesef aşağıdaki cevapların hiçbiri bunu yapmıyor.


Yanıtlar:


14

Yeniden yönlendirme olmadan veya hiçbir şey olmadan bile >logfile 2>&1, üretim sırasına göre çıktıyı görmeniz garanti edilmez.

Yeni başlayanlar için, uygulamadaki stdout satır tamponlu (tty) veya tamponlu (bir boru hattına) olacaktır, ancak stderr tamponsuzdur, bu nedenle çıktı sırası arasındaki ilişkiler bir okuyucuyla ilgili olarak kopar. Herhangi bir boru hattında takip edeceğiniz sonraki aşamalar, iki akıma kararsız bir şekilde erişilmeyecektir (kavramsal olarak paralel olan şeylerdir ve okuyucunuz bir dilime sahipse, yazarın zaten bir dilimine sahipse) her iki boruya da yazılmış, hangisinin önce geldiğini söyleyemezsiniz.

“[T] O'nun emrini verdi” sadece başvuru tarafından biliniyor. Stdout / stderr üzerinden çıktının sıralanması iyi bilinen - klasik, belki - bir sorundur.


7

Eğer inşaat kullandığınızda: 1>stdout.log 2>&1Her iki Stderr ve stdout'u dosyaya olsun Yönlendirilen çünkü stdout'u yönlendirme önce ayarlanmış şekilde hataları yeniden yönlendirme.

Siparişin invert sen alabilirsiniz Eğer stdout'u bir dosyaya yönlendirilir ve daha sonra kopya stderr'yi için stdout Eğer boru o gidebilmeleri için tee.

$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2

$ ./test 2>&1 1>stdout.log | tee stderr.log
ERR!

$ cat stdout.log
OUT!

$ cat stderr.log
ERR!

Bu, iki akışı aynı dosyaya kaydetmenize izin vermiyor.
artistoex

1
@ artistoex: tee -aAynı dosyadaki 1>her iki çıktıyı toplamak için kullanılan aynı dosya ile kullanabilirsiniz . Gibi bir şey:./test 2>&1 1>out+err.log | tee -a out+err.log
mmoya

Neden olduğunu bilmiyorum ama bu wget -O - www.google.debenim için işe yaramıyor . (aşağıdaki çözümle karşılaştırın)
artistoex

4
tee -aHiçbir şey yönlendirilmediyse, konsolda olacak aynı çıktının elde edilmesi için kullanmak güvenilir bir şekilde çalışmaz. Aracılığıyla gider çıkışı teebiraz geç günlüğüne görünebilir böylece komuta doğrudan gelir ve çıkışı ile karşılaştırıldığında gecikebilir.
Gilles 'SO- kötülük olmayı bırak'

Bu benim için işe yaramıyor, dışarı + err.log boştur. Ve evet, aynı dosyada başıboş ve standart olmak istiyorum
Andreas

7

Bunu, bash betiğinin en üstüne şu satırı yerleştirerek başarabildim:

exec 1>>log 2> >(tee -a log >&2)

Bu stdout'u dosyaya log( 1>>log) yönlendirir , ardından tee stderr'i dosyaya log( 2> >(tee -a log) yönlendirir ve onu stderr'e yönlendirir ( >&2). Bu şekilde, logstdout ve stderr'yi sırasıyla gösteren tek bir dosya alıyorum ve stderr da Her zamanki gibi ekranda görüntülenir.

Catch, sadece dosyaya eklediğimde çalışıyor gibi görünüyor. Eğer eklemezsem, iki yönlendirmenin birbirini tıkadığı anlaşılıyor ve hangisinin en son çıkışını görüyorum.


Bunun, STDERR satırlarını özel bir dizge içerecek şekilde "değiştirmesini" sağlamanın herhangi bir yolu var;
IMTheNachoMan

1

Hata akışını hem konsolda hem de günlük dosyasında görünecek şekilde çoğaltmak istersiniz. Bunun için araç teeve yapmanız gereken tek şey onu hata akışına uygulamak. Ne yazık ki, bir komutun hata akışını başka bir komuta aktarmak için standart bir kabuk yapısı yoktur, bu nedenle küçük bir dosya tanımlayıcı düzenlemesi gerekir.

{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^   ^^^^^^^^^^^^    ^^^^^^^^^
  command produces output      stdout3                    log
  command produces error       stderr1   dup to terminal  log

Bu da benim için pek işe yaramıyor, ilk önce stdout satırları alıyorum ve ardından 'log' dosyasındaki tüm stderr satırlarını alıyorum. örnek: {{echo out; echo err 1> 2; echo out2; echo err2 1 & 2; } 2> & 1> & 3 | tee / dev / tty; }> log 3> & 1
Andreas

1
@Andreas: Ah, ah, teeburada da bir gecikme yaşanıyor . Saf bir kabuk çözümü olduğunu sanmıyorum. Aslında, uygulamayı bir şekilde değiştirmeden bir çözüm olup olmadığını merak ediyorum.
Gilles 'SO- kötülük' dur

1

Stderr'i ekrana yazmak ve BOTH stderr ve stdout'u bir dosyaya yazmak - VE stderr ve stdout satırlarının her ikisi de ekrana yazılsalar, aynı sırayla ortaya çıkar:

Özellikle ekrana yazdıysanız, özellikle "aynı diziye" sahip olmayla ilgili kısmı zor bir problem olarak ortaya çıkıyor. Basit bir ifadeyle: Her birini kendi dosyasına yazın, her bir satırı (her dosyadaki) satırın tam olarak üretildiği zamanla işaretlemek için bazı arka plan işlem sihirleri uygulayın ve ardından: "tail --follow" stderr dosyasını ekrana getirin , ancak BOTH "stderr" ve "stdout" u birlikte görmek için - sırayla - iki dosyayı (her satırda tam zamanlı işaretlerle) birlikte sıralayın.

Kod:

# Set the location of output and the "first name" of the log file(s)
pth=$HOME
ffn=my_log_filename_with_no_extension
date >>$pth/$ffn.out
date >>$pth/$ffn.err
# Start background processes to handle 2 files, by rewriting each one line-by-line as each line is added, putting a label at front of line
tail -f $pth/$ffn.out | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|1|".$_' >>$pth/$ffn.out.txt &
tail -f $pth/$ffn.err | perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|2|".$_' >>$pth/$ffn.err.txt &
sleep 1
# Remember the process id of each of 2 background processes
export idout=`ps -ef | grep "tail -f $pth/$ffn.out" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
export iderr=`ps -ef | grep "tail -f $pth/$ffn.err" | grep -v 'grep' | perl -pe 's/\s+/\t/g' | cut -f2`
# Run the command, sending stdout to one file, and stderr to a 2nd file
bash mycommand.sh 1>>$pth/$ffn.out 2>>$pth/$ffn.err
# Remember the exit code of the command
myexit=$?
# Kill the two background processes
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"idout"}/'
echo kill $idout
kill $idout
ps -ef | perl -lne 'print if m/^\S+\s+$ENV{"iderr"}/'
echo kill $iderr
kill $iderr
date
echo "Exit code: $myexit for '$listname', list item# '$ix', bookcode '$bookcode'"

Evet, bu ayrıntılı görünüyor ve 4 çıktı dosyasıyla sonuçlanıyor (bunlardan 2'sini silebilirsiniz). Bunun çözülmesi zor bir sorun olduğu anlaşılıyor, bu yüzden birkaç mekanizma aldı.

Sonunda, beklediğiniz sırada BOTH stdout ve stderr sonuçlarını görmek için şunu çalıştırın:

cat $pth/$ffn.out.txt $pth/$ffn.err.txt | sort

Dizinin, en azından hem stdout hem de stderr'e sahip olacağına yakın olmasının tek nedeni, ekrana basitçe gitmesidir: Her satır, milisaniyeye kadar olan bir zaman damgası ile işaretlenir.

İşlem devam ederken ekranda stderr'i görmek için şunu kullanın:

tail -f $pth/$ffn.out

Asıl soru sorulduktan uzun süre sonra buraya gelen birine yardım edecek umuduyla.


Çaba için +1, benzer bir fikrim vardı (her iki akışı da perl aracılığıyla basıyor) ama uygulamak için uğraşıyordum. Benim için işe yarayıp yaramadığını araştıracağım (yani, gerçekten çıktıların sırasını koruyorsa, buradaki diğer çözümler burada stderr ile stdout etiketleme arasındaki eşzamansızlık nedeniyle işleri biraz değiştirir)
Olivier Dulac

0

Yürütülmesini fistediğiniz komut olsun, o zaman bu

( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )

sana ne istersen vermelisin. Örneğin wget -O - www.google.deşuna benzer:

( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )

1
Bu neredeyse benim için çalışıyor, ancak günlük dosyasında önce tüm stdout satırlarını sonra da tüm stderror satırlarını alıyorum. Onların olduğu gibi doğal sırada araya girmelerini istiyorum.
Andreas

0

Burada okuduklarım göz önüne alındığında, görebildiğim tek çözüm, STOUT veya STERR öğelerini bir zaman dilimi / timestamp ile hazırlamak olacaktır. date +% s saniyeye varacak, ancak siparişi korumak için yavaşlayacak. Ayrıca kayıt bir şekilde sıralanmalıydı. Yapabildiğimden daha fazla iş.


0

Bu kesin gereksinimle mücadele ettim ve sonunda basit bir çözüm bulamadım. Sonunda yaptığım şey şuydu:

TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE

Stdout ve stderr, uygun bir şekilde birleştirilmiş sırayla günlük dosyasına ekler. Ve herhangi bir hata meydana gelirse (komutun çıkış durumuna göre belirlendiğinde), tüm çıktıyı (stdout ve stderr) tekrar doğru bir araya eklenmiş sırayla stdout'a gönderir . Bunu ihtiyaçlarım için makul bir uzlaşma olarak buldum. Büyüyen bir çoklu çalıştırma dosyası yerine tek çalıştırmalı bir günlük dosyası istiyorsanız, daha da basit:

myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log

0

komut değiştirilmiş stderr tee -ave aynı dosyaya eklenen stdout:

./script.sh 2> >(tee -a outputfile) >>outputfile

ve not: çok doğru bir sıra (ancak stderr-show olmadan) sağlayın, tty'yi simule eden ve terminalde göründüğü gibi stdout / err tutan 'beklenti' araçlarından 'unbuffer' komutu var:

unbuffer ./script.sh > outputfile
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.