การเขียนเอาต์พุตไปยังล็อกไฟล์และคอนโซล


101

ใน Unix shell ฉันมีไฟล์ env (ไฟล์env กำหนดพารามิเตอร์ที่จำเป็นสำหรับการรันสคริปต์ผู้ใช้เช่นชื่อไฟล์บันทึกและเส้นทางเอาต์พุตการเปลี่ยนเส้นทางและข้อผิดพลาดไปยังไฟล์บันทึกรายละเอียดการเชื่อมต่อฐานข้อมูล ฯลฯ ) ซึ่งเปลี่ยนทิศทางเอาต์พุตทั้งหมด ( ข้อความสะท้อน ) และข้อผิดพลาดกับไฟล์บันทึกจากสคริปต์ที่เรียกใช้งานโดยใช้รหัสต่อไปนี้:

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

ไฟล์ env ถูกเรียกใช้ที่จุดเริ่มต้นของแต่ละสคริปต์ เนื่องจากโค้ดด้านบนในไฟล์ env เอาต์พุตคอนโซลทั้งหมดที่อาจเป็นเอาต์พุตของผู้ใช้หรือข้อผิดพลาดจะถูกส่งออกไปยังไฟล์บันทึกโดยตรงซึ่งเป็นสิ่งที่ฉันต้องการจริงๆ

แต่มีเอาต์พุตของผู้ใช้ที่เลือกซึ่งฉันต้องการให้แสดงทั้งในคอนโซลและล็อกไฟล์ แต่เนื่องจากรหัสด้านบนฉันไม่สามารถทำได้

ฉันรู้ว่าถ้าฉันลบโค้ดด้านบนฉันจะได้ผลลัพธ์ที่ต้องการสำหรับกรณีนี้ แต่ฉันจะต้องเขียนผลลัพธ์อื่น ๆ ทั้งหมดลงในไฟล์บันทึกด้วยตนเองซึ่งไม่ใช่เรื่องง่าย

มีวิธีรับเอาต์พุตทั้งในคอนโซลและล็อกไฟล์โดยไม่ต้องลบโค้ดด้านบนหรือไม่

คำตอบ:


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

จะส่งเอาต์พุต stdout และ stderr ไปยังไฟล์บันทึก แต่จะปล่อยให้คุณเชื่อมต่อ fd 3 กับคอนโซลดังนั้นคุณสามารถทำได้

echo "Some console message" 1>&3

เพื่อเขียนข้อความไปยังคอนโซลหรือ

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

การเขียนข้อความไปยังทั้งคอนโซลและแฟ้มบันทึก - teeส่งออกของทั้ง FD ของตัวเอง 1 (ซึ่งที่นี่เป็นLOG_FILE) และไฟล์ที่คุณบอกว่ามันจะเขียนถึง (ซึ่งในที่นี้คือ FD 3 คือคอนโซล)

ตัวอย่าง:

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

จะพิมพ์

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

บนคอนโซลและวาง

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

ลงในล็อกไฟล์


2
มันได้ผลตามที่คุณแนะนำ แต่ผมไม่เข้าใจที / dev / FD / 3 ฉันรู้ว่าทีเขียนข้อความไปยังล็อกไฟล์และคอนโซล แต่ฉันไม่เข้าใจ/ dev / fd / 3 ที่ใช้หลังจากที
abinash shrestha

@shrestha เป็นการใช้ทีผิดปกติฉันเห็นด้วย /dev/fd/3เป็นชื่อไฟล์ที่หมายถึง "เปิดขณะ FD 3" ดังนั้นtee /dev/fd/3จะเขียนสิ่งที่มาถึงใน stdin เพื่อ fd ที่ 1 และยัง fd 3 (คน/dev/fd/3ไฟล์) Fd 1 เชื่อมต่อกับล็อกไฟล์ fd 3 เชื่อมต่อกับคอนโซล
Ian Roberts

นอกจากนี้คุณต้องการเพียงแค่เขียนลงไฟล์และไม่ลองใช้คอนโซล: echo "blah" | tee file1.txt | tee file2.txt >/dev/null "Blah" จะไม่ถูกใส่ใน file1.txt & file2.txt แต่จะไม่เขียนลงในคอนโซล
อันตราย 89

สิ่งนี้มีประโยชน์มากสำหรับฉัน แม้ว่าฉันจะสังเกตเห็นบางอย่างที่อาจไม่ตรงประเด็น แต่อาจมีบางคนที่รู้สาเหตุของเรื่องนี้ ฉันเรียกใช้สคริปต์ R จากสคริปต์ทุบตี เอาต์พุตคอนโซลของฟังก์ชัน R ต่างๆเป็นสี (ตามที่ฉันกำหนดไว้) เมื่อใช้วิธีการข้างต้นเพื่อเปลี่ยนเส้นทางเอาต์พุตไปยังคอนโซล AND logfile เอาต์พุตคอนโซลจะไม่มีสีอีกต่อไป อะไรคือสาเหตุของสิ่งนั้น?
dieHellste

1
@dieHellste บางโปรแกรมสามารถตรวจจับได้เมื่อเอาต์พุตถูกส่งไปยังกระบวนการอื่น (ในกรณีนี้teeซึ่งจะเขียนไปยังเทอร์มินัล) แทนที่จะไปที่เทอร์มินัลโดยตรงและปรับเอาต์พุตให้ตรงกัน
Ian Roberts

43

ใช่คุณต้องการใช้tee:

ที - อ่านจากอินพุตมาตรฐานและเขียนไปยังเอาต์พุตและไฟล์มาตรฐาน

เพียงแค่ไพพ์คำสั่งของคุณไปที่ทีและส่งไฟล์เป็นอาร์กิวเมนต์ดังนี้:

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

สิ่งนี้ทั้งสองพิมพ์เอาต์พุตไปยัง STDOUT และเขียนเอาต์พุตเดียวกันลงในล็อกไฟล์ ดูman teeข้อมูลเพิ่มเติม

โปรดทราบว่าสิ่งนี้จะไม่เขียน stderr ลงในไฟล์บันทึกดังนั้นหากคุณต้องการรวมสองสตรีมให้ใช้:

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

2
วิธีแก้ปัญหาข้างต้นไม่ได้ผล ฉันมีไฟล์ redirect.env เป็น: ##### redirect.env ###### export LOG_FILE = log.txt exec 1 2> & 1 | ที -a $ {LOG_FILE} exec 1 | ที -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} ######### และไฟล์ที่ใช้งานมีรหัสต่อไปนี้: ##### output.sh ##### #! / bin / sh redirect.env echo "ผลลัพธ์ที่ถูกต้อง" ech "ผลลัพธ์ที่ไม่ถูกต้อง" ############### แต่ฉันได้รับข้อผิดพลาด: #### redirect.env: บรรทัด 3: exec: 1: ไม่พบ redirect.env: บรรทัด 5: exec: 1: not found redirect.env: line 6: exec: 2: not found #### และในไฟล์บันทึกฉันได้รับข้อผิดพลาดเดียวกันเช่นกัน ฉันทำอะไรผิดหรือเปล่า?
abinash shrestha

ยากมากที่จะบอกได้เนื่องจากบรรทัดใหม่ถูกขีดฆ่าในความคิดเห็นของคุณ คุณช่วยเพิ่มการวางโค้ดในที่อื่นเช่นส่วนสำคัญได้ไหม
Jon Cairns

1
ฉันได้เพิ่มไฟล์ที่จะเชื่อมโยงUnixRedirect ไฟล์ที่เกี่ยวข้องคือ redirect.env และ output.sh
abinash shrestha

1
ดูเหมือนว่ารหัสนี้จะใช้งานไม่ได้ (อย่างน้อยก็ไม่มีอีกแล้ว) คุณมักจะได้รับscript.sh: line 5: exec: 1: not found
tftd

40

ฉันลองใช้คำตอบของ joonty แต่ฉันก็ได้รับไฟล์

exec: 1: ไม่พบ

ข้อผิดพลาด นี่คือสิ่งที่ดีที่สุดสำหรับฉัน ( ยืนยันว่าจะทำงานใน zsh ด้วย):

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

ไฟล์ /tmp/both.log หลังจากนั้นมี

this is stdout
chmmm command not found 

/tmp/both.log ต่อท้ายเว้นแต่คุณจะลบ -a ออกจากที

คำแนะนำ: >(...)เป็นการทดแทนกระบวนการ มันช่วยให้execกับteeคำสั่งราวกับว่ามันเป็นไฟล์


1
สิ่งนี้เป็นเสน่ห์สำหรับฉันในขณะที่คำตอบอื่น ๆ ได้รับผลกระทบหรือพลาด
Jay Taylor

ขอบคุณที่แบ่งปันตัวอย่างข้อมูลนี้ ดูเหมือนว่าจะใช้งานได้ดี (เมื่อเทียบกับคำตอบอื่น ๆ )!
tftd

ใช้งานได้ แต่ตอนนี้เชลล์ของฉันแตกต่างกันหลังจากการดำเนินการ
Josh Usre

@JoshUsre หากคุณต้องการความช่วยเหลือหรือต้องการช่วยเหลือผู้ใช้ SO อื่น ๆ โปรดอธิบายว่าอะไรแตกต่างกันและอธิบายเชลล์เวอร์ชัน OS และอื่น ๆ ของคุณ
alfonx

1
หากคุณไม่ต้องการแยกความแตกต่างระหว่าง stdout และ stderr คุณสามารถรวมคำสั่งเข้าexec > >(tee ${LOG_FILE}) 2>&1ด้วยกันได้
darkdragon

6

ฉันต้องการแสดงบันทึกบน stdout และล็อกไฟล์พร้อมกับการประทับเวลา คำตอบข้างต้นไม่ได้ผลสำหรับฉัน ฉันใช้การทดแทนกระบวนการและคำสั่งexecและสร้างรหัสต่อไปนี้ บันทึกตัวอย่าง:

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

เพิ่มบรรทัดต่อไปนี้ที่ด้านบนของสคริปต์ของคุณ:

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)

หวังว่านี่จะช่วยใครสักคน!


เป็นไปได้ไหมที่จะบันทึกข้อผิดพลาดของแอปพลิเคชันทั้งหมดเพื่อเข้าสู่ไดเรกทอรีโฮสต์เพื่อที่จะช่วยให้ผู้พัฒนา ....
Prasad Shinde

3

สำหรับไฟล์บันทึกคุณอาจต้องป้อนข้อมูลข้อความ รหัสต่อไปนี้อาจช่วยได้

# 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  

ouput: 2. ไฟล์บันทึกจะถูกสร้างขึ้นในการรันครั้งแรกและอัปเดตต่อไปจากการรันครั้งต่อไป ในกรณีที่ไฟล์บันทึกหายไปในการทำงานในอนาคตสคริปต์จะสร้างไฟล์บันทึกใหม่


1

ฉันพบวิธีที่จะได้ผลลัพธ์ที่ต้องการแล้ว แม้ว่ามันอาจจะค่อนข้างนอกรีตก็ตาม ยังไงก็ตามที่นี่ ในไฟล์ redir.env ฉันมีรหัสต่อไปนี้:

#####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}
    }

จากนั้นในสคริปต์จริงฉันมีรหัสต่อไปนี้:

#!/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

ในที่นี้echo จะส่งออกไปยังคอนโซลเท่านั้นล็อกเอาต์พุตจะแสดงเฉพาะไฟล์บันทึกและเอาต์พุตข้อความไปยังทั้งล็อกไฟล์และคอนโซล

หลังจากเรียกใช้ไฟล์สคริปต์ข้างต้นฉันมีผลลัพธ์ต่อไปนี้:

ในคอนโซล

ในคอนโซล
Echoed ไปยังคอนโซลเท่านั้น
เพื่อคอนโซลและบันทึก

สำหรับไฟล์บันทึก

ในไฟล์บันทึก เขียนลงในล็อกไฟล์เท่านั้น
นี่คือ stderr เขียนลงในล็อกไฟล์เท่านั้น
เพื่อคอนโซลและบันทึก

หวังว่าจะช่วยได้


1

ลองสิ่งนี้มันจะทำงาน:

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

สิ่งนี้exec > >(tee -a ${log_file} )เหมาะกับความต้องการของฉัน วิธีแก้ปัญหาก่อนหน้านี้ข้างต้นจะขัดจังหวะส่วนของสคริปต์ของฉันที่บังคับให้ออกหากล้มเหลว ขอบคุณ
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

ฉันพบว่ามีประโยชน์มากในการผนวกทั้ง stdout และ stderr เข้ากับไฟล์บันทึก ผมก็ดีใจที่ได้เห็นวิธีการแก้ปัญหาโดย alfonx ด้วยเพราะผมสงสัยว่าจะบรรลุนี้โดยใช้exec > >(tee -a) execฉันเจอโซลูชันที่สร้างสรรค์โดยใช้ไวยากรณ์ here-doc และ.: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell - สคริปต์

ฉันค้นพบว่าใน zsh โซลูชัน here-doc สามารถแก้ไขได้โดยใช้โครงสร้าง "multios" เพื่อคัดลอกผลลัพธ์ไปยังทั้ง stdout / stderr และไฟล์บันทึก:

#!/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โซลูชัน แต่มีข้อดีคือช่วยให้คุณสามารถบันทึกเพียงบางส่วนของสคริปต์ได้ แน่นอนว่าหากคุณละ EOF สคริปต์ทั้งหมดจะถูกดำเนินการด้วยการบันทึก ผมไม่แน่ใจว่าวิธีzshการดำเนินการ multios teeแต่มันอาจจะมีค่าใช้จ่ายน้อยกว่า น่าเสียดายที่ดูเหมือนว่าจะไม่สามารถใช้มัลติกับexecไฟล์.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.