Ghi kết quả đầu ra vào tệp nhật ký và bảng điều khiển


98

Trong Unix shell, tôi có một tệp env (tệp env xác định các tham số cần thiết để chạy tập lệnh người dùng như tên và đường dẫn tệp nhật ký, chuyển hướng đầu ra và lỗi đối với tệp nhật ký, chi tiết kết nối cơ sở dữ liệu, v.v. ) chuyển hướng tất cả các đầu ra ( thông báo tiếng vang ) và lỗi đối với tệp nhật ký từ tập lệnh được thực thi bằng cách sử dụng mã sau:

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

Tệp env được thực thi ở đầu mỗi tập lệnh. Do đoạn mã trên trong tệp env, tất cả các kết quả đầu ra của bảng điều khiển có thể là kết quả đầu ra của người dùng hoặc lỗi được xuất trực tiếp vào tệp nhật ký, đó là những gì tôi thực sự cần.

Nhưng có một số đầu ra do người dùng chọn lọc mà tôi muốn được hiển thị trong cả bảng điều khiển và tệp nhật ký. Nhưng vì mã trên tôi không thể làm như vậy.

Tôi biết rằng nếu tôi loại bỏ đoạn mã trên, tôi có thể nhận được kết quả mong muốn cho trường hợp này, nhưng tôi sẽ phải ghi thủ công tất cả các đầu ra khác vào tệp nhật ký, đây không phải là một nhiệm vụ dễ dàng.

Có cách nào để lấy đầu ra trong cả bảng điều khiển và tệp nhật ký mà không xóa các mã trên không?

Câu trả lời:


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

sẽ gửi đầu ra stdout và stderr vào tệp nhật ký, nhưng cũng sẽ để lại cho bạn fd 3 được kết nối với bảng điều khiển, vì vậy bạn có thể làm

echo "Some console message" 1>&3

để viết một tin nhắn chỉ vào bảng điều khiển, hoặc

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

để viết thông báo cho cả bảng điều khiển tệp nhật ký - teegửi đầu ra của nó đến cả fd 1 của chính nó (ở đây là LOG_FILE) và tệp bạn đã yêu cầu nó ghi vào (ở đây là fd 3, tức là bảng điều khiển).

Thí dụ:

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

sẽ in

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

trên bảng điều khiển và đặt

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

vào tệp nhật ký.


2
Nó hoạt động đúng như bạn đề xuất. Nhưng tôi không hiểu tee / dev / fd / 3 . Tôi biết rằng tee viết tin để ghi log file và giao diện điều khiển, nhưng tôi đã không hoàn toàn hiểu được / dev / fd / 3 sử dụng sau khi tee
Shrestha abinash

@shrestha đó là một cách sử dụng tee không bình thường, tôi đồng ý. Đây /dev/fd/3là tên tệp đề cập đến "fd 3 hiện đang mở", vì vậy tee /dev/fd/3sẽ viết bất cứ điều gì đến trên stdin của nó thành fd 1 và cả fd 3 ( /dev/fd/3tệp). Fd 1 được kết nối với tệp nhật ký, fd 3 được kết nối với bảng điều khiển.
Ian Roberts

Ngoài ra, bạn chỉ muốn ghi vào một tệp chứ không phải bảng điều khiển, hãy thử: echo "blah" | tee file1.txt | tee file2.txt >/dev/null 'Blah' sẽ không được đưa vào tệp1.txt & tệp2.txt, nhưng không được ghi vào bảng điều khiển.
nguy hiểm 89

Điều này rất hữu ích cho tôi. Mặc dù tôi nhận thấy điều gì đó có thể lạc đề nhưng có lẽ một số bạn biết lý do của việc này. Tôi đang thực thi các tập lệnh R từ một tập lệnh bash. Đầu ra bảng điều khiển của các chức năng R khác nhau được tô màu (như tôi đã định nghĩa). khi sử dụng cách tiếp cận trên để chuyển hướng đầu ra đến console AND logfile, đầu ra console không có màu nữa. Điều gì có thể là lý do cho điều đó?
dieHellste

1
@dieHellste một số chương trình có thể phát hiện khi nào đầu ra của chúng đang được chuyển đến một quy trình khác (trong trường hợp này tee, quá trình này sẽ ghi vào thiết bị đầu cuối) thay vì đi trực tiếp vào một thiết bị đầu cuối và điều chỉnh đầu ra của chúng cho phù hợp.
Ian Roberts

43

Có, bạn muốn sử dụng tee:

tee - đọc từ đầu vào tiêu chuẩn và ghi vào đầu ra và tệp tiêu chuẩn

Chỉ cần chuyển lệnh của bạn đến tee và chuyển tệp dưới dạng đối số, như sau:

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

Điều này vừa in kết quả ra STDOUT vừa ghi cùng một đầu ra vào tệp nhật ký. Xemman tee để biết thêm thông tin.

Lưu ý rằng điều này sẽ không ghi stderr vào tệp nhật ký, vì vậy nếu bạn muốn kết hợp hai luồng thì hãy sử dụng:

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

2
Các giải pháp trên đã không hoạt động. Tôi có tệp redirect.env là: ##### redirect.env ###### export LOG_FILE = log.txt executive 1 2> & 1 | tee -a $ {LOG_FILE} thực thi 1 | tee -a $ {LOG_FILE} thực thi 2 | tee -a $ {LOG_FILE} ######### và tệp làm việc chứa mã sau: ##### output.sh ##### #! / bin / sh. redirect.env echo "Đầu ra hợp lệ" ech "đầu ra không hợp lệ" ############## nhưng tôi gặp lỗi: #### redirect.env: dòng 3: thi hành: 1: không tìm thấy redirect.env: dòng 5: thi hành: 1: không tìm thấy redirect.env: dòng 6: thực thi: 2: không tìm thấy #### và trong tệp nhật ký, tôi cũng gặp lỗi tương tự. Tôi có làm gì sai không?
abinash shrestha

Rất khó để nói, vì những dòng mới được lược bỏ trong bình luận của bạn. Bạn có thể dán mã vào một nơi nào đó như ý chính không?
Jon Cairns,

1
Tôi đã thêm các tệp vào liên kết UnixRedirect . Các tập tin có liên quan là redirect.env và output.sh
Shrestha abinash

1
Mã này dường như không hoạt động (ít nhất là không còn nữa). Bạn thường sẽ nhận đượcscript.sh: line 5: exec: 1: not found
tftd

39

Tôi đã thử câu trả lời của joonty, nhưng tôi cũng nhận được

thi hành: 1: không tìm thấy

lỗi. Đây là những gì phù hợp nhất với tôi ( được xác nhận cũng làm việc trong zsh):

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

Tệp /tmp/both.log sau đó chứa

this is stdout
chmmm command not found 

/Tmp/both.log được thêm vào trừ khi bạn xóa -a khỏi tee.

Gợi ý: >(...)là một quá trình thay thế. Nó cho phép execđể các teelệnh như thể nó là một tập tin.


1
Điều này giống như một sự quyến rũ đối với tôi, trong khi các câu trả lời khác đã bị trúng hoặc trượt.
Jay Taylor,

Cảm ơn vì đã chia sẻ đoạn mã này. Điều này dường như đang hoạt động tốt (so với các câu trả lời khác)!
tftd

Điều này hoạt động, nhưng bây giờ shell của tôi đã khác sau khi thực hiện.
Josh Usre 19/02/19

@JoshUsre Nếu bạn muốn bất kỳ sự giúp đỡ hoặc bạn muốn giúp SO-những người dùng khác, xin vui lòng giải thích những gì là khác nhau, và mô tả shell của bạn, phiên bản, hệ điều hành vv ..
alfonx

1
Nếu bạn không cần phân biệt giữa stdout và stderr, bạn có thể kết hợp các câu lệnh thành exec > >(tee ${LOG_FILE}) 2>&1.
darkdragon

5

Tôi muốn hiển thị nhật ký trên stdout và tệp nhật ký cùng với dấu thời gian. Không có câu trả lời nào ở trên phù hợp với tôi. Tôi đã sử dụng thay thế tiến trìnhexec lệnh và đã đưa ra đoạn mã sau. Nhật ký mẫu:

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

Thêm các dòng sau vào đầu tập lệnh của bạn:

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)

Hy vọng điều này sẽ giúp ai đó!


Có thể đăng nhập tất cả các lỗi ứng dụng để đăng nhập vào thư mục của host để nó sẽ giúp devops ....
Prasad Shinde

3

đối với tệp nhật ký, bạn có thể chọn ngày để nhập vào dữ liệu văn bản. mã sau đây có thể hữu ích

# 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. Tệp nhật ký sẽ được tạo trong lần chạy đầu tiên và tiếp tục cập nhật từ lần chạy tiếp theo. Trong trường hợp thiếu tệp nhật ký trong lần chạy sau này, tập lệnh sẽ tạo tệp nhật ký mới.


1

Tôi đã tìm ra cách để có được đầu ra mong muốn. Mặc dù nó có thể là một cách hơi không chính thống. Dù sao ở đây nó sẽ đi. Trong tệp redir.env, tôi có mã sau:

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

Sau đó, trong tập lệnh thực tế, tôi có các mã sau:

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

Ở đây echo chỉ xuất ra bảng điều khiển, log xuất ra chỉ log file và message đầu ra cho cả tập tin đăng nhập và giao diện điều khiển.

Sau khi thực hiện tệp tập lệnh ở trên, tôi có các kết quả sau:

Trong bảng điều khiển

Trong bảng điều khiển Chỉ
vang lên bảng điều khiển
Đến bảng điều khiển và ghi nhật ký

Đối với tệp nhật ký

Trong tệp nhật ký Chỉ được ghi vào tệp nhật ký
Đây là stderr. Chỉ ghi vào tệp nhật ký
Đến bảng điều khiển và nhật ký

Hy vọng điều này giúp đỡ.


1

Hãy thử điều này, nó sẽ thực hiện công việc:

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

Điều này exec > >(tee -a ${log_file} )hoạt động hoàn hảo cho nhu cầu của tôi. Các giải pháp trước đây ở trên sẽ làm gián đoạn các phần của tập lệnh của tôi buộc thoát nếu chúng không thành công. Cảm ơn bạn
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

Tôi thấy rất hữu ích khi nối cả stdout và stderr vào tệp nhật ký. Tôi rất vui khi thấy một giải pháp của alfonx với exec > >(tee -a), bởi vì tôi đang tự hỏi làm thế nào để thực hiện điều này bằng cách sử dụng exec. Tôi đã tìm thấy một giải pháp sáng tạo bằng cách sử dụng cú pháp here-doc và .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -kịch bản

Tôi phát hiện ra rằng trong zsh, giải pháp here-doc có thể được sửa đổi bằng cách sử dụng cấu trúc "multios" để sao chép đầu ra sang cả tệp stdout / stderr và tệp nhật ký:

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

Nó không thể đọc được như execgiải pháp nhưng nó có ưu điểm là cho phép bạn chỉ ghi lại một phần của script. Tất nhiên, nếu bạn bỏ qua EOF thì toàn bộ tập lệnh sẽ được thực thi bằng ghi nhật ký. Tôi không chắc cách zshtriển khai multios, nhưng nó có thể có ít chi phí hơn tee. Thật không may, có vẻ như không thể sử dụng multios với exec.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.