bash बैड स्क्रिप्ट के भीतर से लॉग फ़ाइल करने के लिए stdout की COPY को पुनर्निर्देशित करें




shell redirect (7)

मुझे पता है कि एक फ़ाइल में stdout को पुनर्निर्देशित करने के लिए कैसे:

exec > foo.log
echo test

यह 'test' को foo.log फ़ाइल में डाल देगा।

अब मैं आउटपुट को लॉग फ़ाइल में रीडायरेक्ट करना चाहता हूं और इसे स्टडआउट पर रखना चाहता हूं

यानी इसे स्क्रिप्ट के बाहर से छोटे से किया जा सकता है:

script | tee foo.log

लेकिन मैं इसे स्क्रिप्ट के भीतर ही घोषित करना चाहता हूं

मैंने कोशिश की

exec | tee foo.log

लेकिन यह काम नहीं किया।


इनमें से कोई भी एक आदर्श समाधान नहीं है, लेकिन यहां कुछ ऐसी चीजें हैं जिन्हें आप आजमा सकते हैं:

exec >foo.log
tail -f foo.log &
# rest of your script

या

PIPE=tmp.fifo
mkfifo $PIPE
exec >$PIPE
tee foo.log <$PIPE &
# rest of your script
rm $PIPE

दूसरी बात यह है कि आपकी स्क्रिप्ट के साथ कुछ गलत होने पर एक पाइप फ़ाइल बैठेगी, जो समस्या हो सकती है या नहीं भी हो सकती है (यानी हो सकता है कि आप बाद में इसे मूल खोल में rm कर सकें)।


बैश 4 में एक coproc कमांड है जो एक कमांड पर नामित पाइप स्थापित करता है और आपको इसके माध्यम से संवाद करने की अनुमति देता है।


व्यस्त बॉक्स और गैर-बैश गोले के लिए समाधान

स्वीकार किए गए उत्तर निश्चित रूप से बैश के लिए सबसे अच्छा विकल्प है। मैं बैश तक पहुंच के बिना एक व्यस्त बॉक्स वातावरण में काम कर रहा हूं, और यह exec > >(tee log.txt) वाक्यविन्यास को समझ में नहीं आता है। यह exec >$PIPE के समान नाम के साथ सामान्य फ़ाइल बनाने की कोशिश कर रहा है, जो ठीक से exec >$PIPE नहीं करता है, जो विफल रहता है और लटकता है।

उम्मीद है कि यह किसी और के लिए उपयोगी होगा जिसके पास बैश नहीं है।

इसके अलावा, नामित पाइप का उपयोग करने वाले किसी भी व्यक्ति के लिए, यह rm $PIPE लिए सुरक्षित है, क्योंकि यह वीएफएस से पाइप को अनलिंक करता है, लेकिन इसका उपयोग करने वाली प्रक्रियाओं को तब तक एक संदर्भ गणना बनाए रखती है जब तक कि वे समाप्त नहीं हो जाते।

ध्यान दें कि $ * का उपयोग जरूरी नहीं है।

#!/bin/sh

if [ "$SELF_LOGGING" != "1" ]
then
    # The parent process will enter this branch and set up logging

    # Create a named piped for logging the child's output
    PIPE=tmp.fifo
    mkfifo $PIPE

    # Launch the child process without redirected to the named pipe
    SELF_LOGGING=1 sh $0 $* >$PIPE &

    # Save PID of child process
    PID=$!

    # Launch tee in a separate process
    tee logfile <$PIPE &

    # Unlink $PIPE because the parent process no longer needs it
    rm $PIPE    

    # Wait for child process running the rest of this script
    wait $PID

    # Return the error code from the child process
    exit $?
fi

# The rest of the script goes here

अपनी स्क्रिप्ट फ़ाइल के अंदर, सभी आदेशों को कोष्ठक के भीतर रखें, जैसे:

(
echo start
ls -l
echo end
) | tee foo.log

स्वीकार्य उत्तर का उपयोग करके मेरी स्क्रिप्ट पृष्ठभूमि में चल रही मेरी बाकी स्क्रिप्ट को छोड़कर असाधारण रूप से जल्दी ('exec>> (tee ...)' के ठीक बाद लौट रही है। चूंकि मुझे अपना रास्ता काम करने के लिए वह समाधान नहीं मिला, मुझे समस्या के आसपास एक और समाधान / काम मिला:

# Logging setup
logfile=mylogfile
mkfifo ${logfile}.pipe
tee < ${logfile}.pipe $logfile &
exec &> ${logfile}.pipe
rm ${logfile}.pipe

# Rest of my script

यह स्क्रिप्ट से आउटपुट को प्रक्रिया से जाता है, पाइप के माध्यम से 'टी' की उप पृष्ठभूमि प्रक्रिया में जो डिस्क पर सब कुछ और स्क्रिप्ट के मूल stdout लॉग करता है।

ध्यान दें कि 'exec &>' दोनों stdout और stderr को पुनर्निर्देशित करता है, अगर हम चाहें तो हम उन्हें अलग से रीडायरेक्ट कर सकते हैं, या 'exec>' में बदल सकते हैं, अगर हम सिर्फ stdout चाहते हैं।

यहां तक ​​कि स्क्रिप्ट की शुरुआत में फ़ाइल सिस्टम से पाइप हटा दिया जाता है, फिर भी प्रक्रिया समाप्त होने तक यह कार्य करना जारी रखेगी। हम आरएम-लाइन के बाद फ़ाइल नाम का उपयोग कर इसका संदर्भ नहीं दे सकते।


#!/usr/bin/env bash

# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
exec > >(tee -i logfile.txt)

# Without this, only stdout would be captured - i.e. your
# log file would not contain any error messages.
# SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
# as a separate stream - I did not want to steal from him by simply
# adding his answer to mine.
exec 2>&1

echo "foo"
echo "bar" >&2

ध्यान दें कि यह bash , नहीं। यदि आप sh myscript.sh साथ स्क्रिप्ट का आह्वान करते हैं, तो आपको syntax error near unexpected token '>' साथ एक त्रुटि मिलेगी।

यदि आप सिग्नल जाल के साथ काम कर रहे हैं, तो आप सिग्नल होने पर आउटपुट के व्यवधान से बचने के लिए tee -i विकल्प का उपयोग करना चाहेंगे। (टिप्पणी के लिए जेम्स थॉमस मून 1 9 7 9 के लिए धन्यवाद।)

उपकरण जो उनके आउटपुट को बदलते हैं, वे एक पाइप या टर्मिनल (उदाहरण के लिए रंगों और स्तंभित आउटपुट का उपयोग करके) लिखते हैं, इस पर निर्भर करते हैं कि उपरोक्त निर्माण का अर्थ यह है कि वे एक पाइप पर आउटपुट करते हैं।

रंगीन / स्तंभकरण को लागू करने के विकल्प हैं (उदाहरण के लिए ls -C --color=always )। ध्यान दें कि इसके परिणामस्वरूप रंग कोड लॉगफाइल पर भी लिखे जा रहे हैं, जिससे इसे कम पठनीय बना दिया जा सकता है।


Syslog पर बैश स्क्रिप्ट लॉग बनाने का आसान तरीका। स्क्रिप्ट आउटपुट /var/log/syslog माध्यम से और stderr के माध्यम से दोनों उपलब्ध है। syslog टाइमस्टैम्प समेत उपयोगी मेटाडेटा जोड़ देगा।

शीर्ष पर इस पंक्ति को जोड़ें:

exec &> >(logger -t myscript -s)

वैकल्पिक रूप से, लॉग को एक अलग फ़ाइल में भेजें:

exec &> >(ts |tee -a /tmp/myscript.output >&2 )

इसके लिए अधिक moreutils आवश्यकता moreutils ( ts कमांड के लिए, जो टाइमस्टैम्प जोड़ता है)।





redirect