Exec ile tüm izleyen komutların stderr'lerini yönlendir


42

Tüm çıktıları bir dosyaya, hata ayıklama günlüğünün yanı sıra terminale yönlendirmem gereken bir bash dosyam var. Hem stdout'u hem de stderr'ı hata ayıklamaya yönlendirmem ve komut dosyasındaki tüm komutlar için kaydetmem gerekiyor.

2>&1 | tee -a $DEBUGDosyadaki her bir komut için eklemek istemiyorum . Ben yaşayabilirim | tee -a $DEBUG.

Böyle bir şeyle yapmanın bir yolu olduğunu hatırlıyorum exec 2>&1.

Şu anda aşağıdaki gibi bir şey kullanıyorum:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

ama bu çalışmıyor. Bir çözümü olan var mı / sebebini açıklayabilir mi?


1
Bazı kabuklarda, |&kısayol olarak çalışır 2>&1 |, en azından biraz daha rahattır.
Kevin

Yanıtlar:


39

Aynı anda birçok komutu yeniden yönlendirecek bir çözüme gelince:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Orijinal çözümünüz neden çalışmıyor: exec 2> & 1 standart hata çıktısını kabuğunuzun standart çıktısına yönlendirecek, komut dosyanızı konsoldan çalıştırırsanız, konsolunuz olacaktır. komutlardaki boru yönlendirme sadece komutun standart çıktısını yönlendirecektir.

Bakış açısına göre somecommand, standart çıktısı bağlı bir boruya teegirer ve standart hata, kabuğun standart çıktısına yönlendirdiğiniz standart kabuk ile aynı dosya / sözde dosyaya gider. Konsolunuzu programdan çalıştırırsanız

Bunu açıklamanın tek yolu gerçekte ne olduğunu görmektir:

Terminalden çalıştırırsanız, kabuğunuzun orijinal ortamı şöyle görünebilir:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Standart hatayı standart çıktıya ( exec 2>&1) yönlendirdikten sonra , temelde hiçbir şeyi değiştiremezsiniz. Ancak senaryonun standart çıktısını bir dosyaya yönlendirirseniz, bunun gibi bir ortam elde edersiniz:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Ardından, kabuk standart hatasını standart çıktıya yönlendirmek şu şekilde sonuçlanır:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Bir komutu çalıştırmak bu ortamı devralacak. Bir komutu çalıştırıp onu tişörte geçirirseniz, komutun ortamı şöyle olacaktır:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Bu yüzden komutunuzun standart hatası, kabuğun standart hatası olarak kullandığı işe devam ediyor.

Komutun ortamını bakarak /proc/[pid]/fdşunları yapabilirsiniz: ls -lsembolik bağlantının içeriğini de listelemek için kullanın. Buradaki 0dosya standart girdi, 1standart çıktı ve 2standart hatadır. Komut daha fazla dosya açarsa (ve çoğu program yaparsa), onları göreceksiniz. Bir program aynı zamanda standart giriş / çıkış ve yeniden kullanımını yönlendirmek veya kapatma seçebilir 0, 1ve 2.


40

Komut dosyanızın üstündeki exec gibi bu kullanabilirsiniz:

exec > >(tee "$HOME/somefile.log") 2>&1

Örneğin:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Bana dosyaya $HOME/somefile.logve bu şekilde terminale çıktı verir :

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye

2
Bunun temel ilkeleri kullandığına dikkat edin - diğer kabuklarda çalışmayabilir (örneğin çizgi). Ancak soru bash, +1 belirtilen beri.
Richard Hansen,

8
@ RichardHansen, işlem değiştirme, bash değil ksh tarafından tanıtılan ve aynı zamanda zsh tarafından desteklenen bir özelliktir, bu yüzden ona bir faşizm demem .
Stéphane Chazelas

6
@StephaneChazelas: İyi bir noktaya değindin. Sadece sözdiziminin POSIX standardı tarafından desteklenmediğini ve dolayısıyla /bin/shsenaryolarda evrensel olarak işe yaramayacağını belirtmek istedim (birçok kişi komut dosyalarında yanlışlıkla bash sözdizimini kullanır /bin/sh).
Richard Hansen,

Benim için bu /dev/fd/62: Operation not supportedherhangi bir ipucu veriyor mu?
Eun

1
Günlük dosyası dışında stderr'i yönlendirmemenin bir yolu var mı? Orijinal senaryo ise myscriptve ben koşmak ./myscript > /dev/null, hala görmelisiniz byegeliyor ki echo bye >&2.
Martin Jambon

0

Stderr ve stdout'u bir dosyaya yazın, stderr'i ekranda görüntüleyin (stdout'ta)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Kırpmalar için kullanışlıdır, böylece posta yoluyla hata (ve yalnızca hata) alabilirsiniz.

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.