Günlük dosyasına ve konsola çıktı yazma


101

Unix kabuğunda, tüm çıktıları ( yankı mesajlarını ) yeniden yönlendiren bir env dosyam var ( env dosyası, günlük dosyası adı ve yolu gibi kullanıcı komut dosyasını çalıştırmak için gerekli parametreleri tanımlar, çıktıları ve hataları günlük dosyasına yönlendir, veritabanı bağlantı ayrıntıları vb. ) ) ve aşağıdaki kod kullanılarak yürütülen komut dosyasından günlük dosyasına yapılan hatalar:

exec 1>>${LOG_FILE}
exec 2>>${LOG_FILE}

Env dosyası her komut dosyasının başında yürütülür. Env dosyasındaki yukarıdaki kod nedeniyle, kullanıcı çıktıları veya hataları olabilecek tüm konsol çıktıları, aslında ihtiyacım olan şey olan günlük dosyasına doğrudan çıktı.

Ancak hem konsolda hem de günlük dosyasında görüntülenmesini istediğim bazı seçici kullanıcı çıktıları var. Ancak yukarıdaki kod nedeniyle bunu yapamıyorum.

Yukarıdaki kodu kaldırırsam bu durumda istenen sonucu alabileceğimi biliyorum, ancak diğer tüm çıktıları manuel olarak günlük dosyasına yazmam gerekecek ki bu kolay bir iş değil.

Yukarıdaki kodları kaldırmadan çıktıyı hem konsolda hem de günlük dosyasında almanın bir yolu var mı?

Yanıtlar:


109
exec 3>&1 1>>${LOG_FILE} 2>&1

stdout ve stderr çıktısını günlük dosyasına gönderir, ancak aynı zamanda konsola bağlı fd 3 ile sizi bırakır, böylece yapabilirsiniz

echo "Some console message" 1>&3

sadece konsola bir mesaj yazmak veya

echo "Some console and log file message" | tee /dev/fd/3

hem konsola hem de günlük dosyasına bir mesaj yazmak için - teeçıktısını hem kendi fd 1'e (buradaki LOG_FILE) hem de yazmasını söylediğiniz dosyaya (burada fd 3, yani konsol) gönderir .

Misal:

exec 3>&1 1>>${LOG_FILE} 2>&1

echo "This is stdout"
echo "This is stderr" 1>&2
echo "This is the console (fd 3)" 1>&3
echo "This is both the log and the console" | tee /dev/fd/3

yazdıracaktı

This is the console (fd 3)
This is both the log and the console

konsolda ve koy

This is stdout
This is stderr
This is both the log and the console

günlük dosyasına.


2
Tam önerdiğiniz gibi çalıştı. Ama tee / dev / fd / 3'ü anlamadım .
Tişörtün

@shrestha, alışılmadık bir tee kullanımı, katılıyorum. Bu /dev/fd/3, "şu anda açık olan fd 3" e atıfta bulunan bir dosya adıdır, bu nedenle tee /dev/fd/3stdinine gelen her şeyi fd 1'e ve ayrıca fd 3'e ( /dev/fd/3dosya) yazacaktır . Fd 1 günlük dosyasına bağlanır, fd 3 konsola bağlıdır.
Ian Roberts

Ayrıca konsola değil, sadece bir dosyaya yazmak istiyorsunuz deneyin: echo "blah" | tee file1.txt | tee file2.txt >/dev/null 'Blah' file1.txt & file2.txt içine koyulmayacak, konsola yazılmayacaktır.
tehlike89

Bu benim için çok yardımcı oldu. Konu dışı olabilecek bir şey fark etsem de belki bazılarınız bunun nedenlerini biliyorsunuzdur. Bir bash betiğinden R betikleri çalıştırıyorum. Çeşitli R fonksiyonlarının konsol çıktısı renklidir (tanımladığım gibi). Çıkışı konsola VE günlük dosyasına yeniden yönlendirmek için yukarıdaki yaklaşımı kullanırken, konsol çıktısı artık renkli değildir. Bunun nedeni ne olabilir?
dieHellste

1
@dieHellste bazı programlar, teedoğrudan bir terminale gitmek yerine , çıktılarının başka bir sürece (bu durumda , bu da terminale yazılır) aktarıldığını algılayabilir ve çıktılarını eşleşecek şekilde ayarlayabilir.
Ian Roberts

43

Evet, kullanmak istiyorsunuz tee:

tee - standart girişten okuyun ve standart çıktıya ve dosyalara yazın

Dosyayı bir argüman olarak tee ve iletmek için komutunuzu yönlendirin, şöyle:

exec 1 | tee ${LOG_FILE}
exec 2 | tee ${LOG_FILE}

Bu, hem çıktıyı STDOUT'a yazdırır hem de aynı çıktıyı bir günlük dosyasına yazar. Daha man teefazla bilgi için bakın .

Bunun günlük dosyasına stderr yazmayacağına dikkat edin, bu nedenle iki akışı birleştirmek istiyorsanız şunu kullanın:

exec 1 2>&1 | tee ${LOG_FILE}

2
Yukarıdaki çözüm işe yaramadı. Redirect.env dosyam şu şekilde var: ##### redirect.env ###### export LOG_FILE = log.txt exec 1 2> & 1 | tee -a $ {LOG_FILE} exec 1 | tee -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} ######### ve çalışma dosyası şu kodu içeriyordu: ##### output.sh ##### #! / bin / sh. redirect.env echo "Geçerli çıktı" ech "geçersiz çıktı" ############### ancak şu hatayı alıyorum: #### redirect.env: satır 3: exec: 1: bulunamadı redirect.env: satır 5: exec: 1: bulunamadı redirect.env: satır 6: exec: 2: bulunamadı #### ve günlük dosyasında da aynı hatayı alıyorum. Yanlış bir şey mi yapıyorum?
abinash shrestha

Yorumunuzda yeni satırlar çıkarıldığı için bunu söylemek çok zor. Öz gibi bir yere kodun bir yapıştırmasını ekleyebilir misiniz ?
Jon Cairns

1
Dosyaları UnixRedirect bağlantısına ekledim . İlgili dosyalar redirect.env ve output.sh
abinash shrestha

1
Bu kod çalışmıyor gibi görünüyor (en azından artık değil). Genelliklescript.sh: line 5: exec: 1: not found
tftd

40

Joonty'nin cevabını denedim ama aynı zamanda

exec: 1: bulunamadı

hata. Bu benim için en iyi olan şeydir ( zsh'da da çalıştığı onaylanmıştır ):

#!/bin/bash
LOG_FILE=/tmp/both.log
exec > >(tee ${LOG_FILE}) 2>&1
echo "this is stdout"
chmmm 77 /makeError

/Tmp/both.log dosyası daha sonra şunları içerir:

this is stdout
chmmm command not found 

/Tmp/both.log, tee'den -a'yı kaldırmadığınız sürece eklenir.

İpucu: >(...)bir süreç ikamesidir. O sağlayan execetmek teebir dosya gibi komuta.


1
Bu benim için bir cazibe merkezi gibi çalıştı, oysa diğer cevaplar tutuldu ya da kaçırıldı.
Jay Taylor

Bu pasajı paylaştığınız için teşekkürler. Bu gayet iyi çalışıyor gibi görünüyor (diğer cevaplarla karşılaştırıldığında)!
tftd

Bu işe yarıyor, ancak şimdi kabuğum yürütmeden sonra farklı.
Josh Usre

@JoshUsre Herhangi bir yardım istiyorsanız veya diğer SO kullanıcılarına yardım etmek istiyorsanız, lütfen neyin farklı olduğunu açıklayın ve kabuğunuzu, sürümünüzü, işletim sisteminizi vb.
Açıklayın

1
Stdout ve stderr arasında ayrım yapmanız gerekmiyorsa, ifadeleri exec > >(tee ${LOG_FILE}) 2>&1.
darkdragon

6

Zaman damgası ile birlikte standart çıkış ve günlük dosyasındaki günlükleri görüntülemek istedim. Yukarıdaki cevapların hiçbiri benim için işe yaramadı. İşlem değiştirme ve exec komutunu kullandım ve aşağıdaki kodu buldum . Örnek günlükler:

2017-06-21 11:16:41+05:30 Fetching information about files in the directory...

Komut dosyanızın üstüne aşağıdaki satırları ekleyin:

LOG_FILE=script.log
exec > >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done)
exec 2> >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done >&2)

Umarım bu birine yardımcı olur!


Devops'a yardımcı olması için ana dizininde oturum açmak için tüm uygulama hatalarını günlüğe kaydetmek mümkün mü ....
Prasad Shinde

3

günlük dosyası için metin verilerini girmek üzere tarih atabilirsiniz. aşağıdaki kod yardımcı olabilir

# declaring variables

Logfile="logfile.txt"   
MAIL_LOG="Message to print in log file"  
Location="were is u want to store log file"

cd $Location   
if [ -f $Logfile ]  
then   
echo "$MAIL_LOG " >> $Logfile

else        

touch $Logfile   
echo "$MAIL_LOG" >> $Logfile    

fi  

çıktı: 2. Günlük dosyası ilk çalıştırmada oluşturulacak ve sonraki çalıştırmalardan itibaren güncellenmeye devam edecektir. Gelecekteki çalıştırmada günlük dosyasının eksik olması durumunda, komut dosyası yeni günlük dosyası oluşturacaktır.


1

İstenilen çıktıyı elde etmenin bir yolunu buldum. Alışılmışın dışında bir yol olsa da. Her neyse işte burada. Redir.env dosyasında aşağıdaki koda sahibim:

#####redir.env#####    
export LOG_FILE=log.txt

      exec 2>>${LOG_FILE}

    function log {
     echo "$1">>${LOG_FILE}
    }

    function message {
     echo "$1"
     echo "$1">>${LOG_FILE}
    }

Sonra gerçek komut dosyasında aşağıdaki kodlara sahibim:

#!/bin/sh 
. redir.env
echo "Echoed to console only"
log "Written to log file only"
message "To console and log"
echo "This is stderr. Written to log file only" 1>&2

Burada yankı sadece konsola çıkışlarını log yalnızca dosya ve giriş yapmak için çıkışlar mesajı günlük dosyası ve konsola ilişkin çıkışları.

Yukarıdaki komut dosyasını çalıştırdıktan sonra aşağıdaki çıktılara sahibim:

Konsolda


Konsolda sadece konsola yankılandı
Konsola ve günlüğe

Günlük dosyası için

Günlük Dosyasında Yalnızca günlük dosyasına yazılmıştır
Bu stderr'dir. Yalnızca günlük dosyası için yazılmıştır
Konsol ve günlüğe

Umarım bu yardımcı olur.


1

Bunu dene, işi yapacak:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)

Bu, exec > >(tee -a ${log_file} )ihtiyaçlarım için mükemmel çalışıyor. Yukarıdaki önceki çözümler, betiğimin başarısız olması durumunda çıkmaya zorlayan bölümlerini kesintiye uğratıyordu. Teşekkürler
MitchellK

1
    #
    #------------------------------------------------------------------------------
    # echo pass params and print them to a log file and terminal
    # with timestamp and $host_name and $0 PID
    # usage:
    # doLog "INFO some info message"
    # doLog "DEBUG some debug message"
    # doLog "WARN some warning message"
    # doLog "ERROR some really ERROR message"
    # doLog "FATAL some really fatal message"
    #------------------------------------------------------------------------------
    doLog(){
        type_of_msg=$(echo $*|cut -d" " -f1)
        msg=$(echo "$*"|cut -d" " -f2-)
        [[ $type_of_msg == DEBUG ]] && [[ $do_print_debug_msgs -ne 1 ]] && return
        [[ $type_of_msg == INFO ]] && type_of_msg="INFO " # one space for aligning
        [[ $type_of_msg == WARN ]] && type_of_msg="WARN " # as well

        # print to the terminal if we have one
        test -t 1 && echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg"

        # define default log file none specified in cnf file
        test -z $log_file && \
            mkdir -p $product_instance_dir/dat/log/bash && \
                log_file="$product_instance_dir/dat/log/bash/$run_unit.`date "+%Y%m"`.log"
        echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg" >> $log_file
    }
    #eof func doLog

0

Bir günlük dosyasına hem stdout hem de stderr eklemeyi çok yararlı buluyorum. Alfonx ile bir çözüm gördüğüme sevindim exec > >(tee -a), çünkü bunu kullanarak nasıl başaracağımı merak ediyordum exec. Here-doc sözdizimini kullanan yaratıcı bir çözüme rastladım ve .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -senaryo

Zsh'da, here-doc çözümünün çıktıyı hem stdout / stderr'e hem de günlük dosyasına kopyalamak için "multios" yapısı kullanılarak değiştirilebileceğini keşfettim:

#!/bin/zsh
LOG=$0.log
# 8 is an arbitrary number;
# multiple redirects for the same file descriptor 
# triggers "multios"
. 8<<\EOF /dev/fd/8 2>&2 >&1 2>>$LOG >>$LOG
# some commands
date >&2
set -x
echo hi
echo bye
EOF
echo not logged

execÇözüm kadar okunabilir değildir, ancak betiğin sadece bir bölümünü günlüğe kaydetmenize izin verme avantajına sahiptir. Elbette, EOF'yi atlarsanız, tüm komut dosyası günlük kaydı ile yürütülür. zshMultios'u nasıl uyguladığından emin değilim , ancak daha az ek yükü olabilir tee. Maalesef öyle görünüyor ki, multios ile kullanılamaz exec.

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.