बैश स्क्रिप्ट के भीतर से ही लॉग फाइल करने के लिए stdout की पुनर्निर्देशित कॉपी


235

मुझे पता है कि किसी फ़ाइल पर stdout रीडायरेक्ट कैसे करें :

exec > foo.log
echo test

यह foo.log फ़ाइल में 'परीक्षण' डालेगा।

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

अर्थात यह स्क्रिप्ट के बाहर से तुच्छ रूप से किया जा सकता है:

script | tee foo.log

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

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

exec | tee foo.log

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


3
आपका प्रश्न खराब तरीके से संचालित है। जब आप 'निष्पादित> foo.log' को लागू करते हैं, तो स्क्रिप्ट का स्टडआउट फ़ाइल foo.log है। मुझे लगता है कि आपका मतलब है कि आप चाहते हैं कि आउटपुट foo.log और tty पर जाए, क्योंकि foo.log पर जाने से stdout होने वाला है।
विलियम पर्ससेल

मैं क्या करना चाहते हैं का उपयोग करना है | 'निष्पादन' पर। मेरे लिए यह सही होगा, यानी "एक्ज़ीक्यूट। टी फू फू.लॉग", दुर्भाग्य से आप एक्ज़ीक्यूट कॉल पर पाइप पुनर्निर्देशन का उपयोग नहीं कर सकते हैं
विटाली कुशनर

जवाबों:


297
#!/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। यदि आप स्क्रिप्ट के साथ आह्वान करते हैं sh myscript.sh, तो आपको एक त्रुटि मिलेगी syntax error near unexpected token '>'

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


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

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


5
अधिकांश प्रणालियों पर टी का बफर है, इसलिए स्क्रिप्ट समाप्त होने के बाद तक आउटपुट नहीं आ सकता है। इसके अलावा, चूंकि यह टी एक सबशेल में चल रही है, न कि एक चाइल्ड प्रोसेस के तौर पर, वेटिंग का उपयोग कॉलिंग प्रोसेस में आउटपुट को सिंक्रोनाइज़ करने के लिए नहीं किया जा सकता है। आप जो चाहते हैं, वह bogomips.org/rainbows.git/commit/… के

14
@ बियर: पोसिक्स निर्दिष्ट करता है कि teeइसके आउटपुट को बफर नहीं करना चाहिए। यदि यह अधिकांश प्रणालियों पर बफर करता है, तो यह अधिकांश प्रणालियों पर टूट जाता है। यह teeमेरे समाधान का नहीं, क्रियान्वयन की समस्या है ।
DevSolar

3
@ सेबेस्टियन: execबहुत शक्तिशाली है, लेकिन बहुत ही शामिल भी है। आप एक अलग फाइल करने वाले को मौजूदा स्टडआउट "बैक अप" कर सकते हैं, फिर बाद में इसे पुनर्प्राप्त कर सकते हैं। Google "बैश निष्पादित ट्यूटोरियल", वहाँ बहुत सारे उन्नत सामान हैं।
देवसोलर

2
@ एडम्सियर्स: मुझे यकीन नहीं है कि बैरी के बारे में क्या था, या तो। बैश का दस्तावेज़ नई प्रक्रियाओं को शुरू नहीं करने के execलिए प्रलेखित किया गया है, >(tee ...)एक मानक नाम पाइप / प्रक्रिया प्रतिस्थापन है, और &पाठ्यक्रम के पुनर्निर्देशन का पृष्ठभूमि से कोई लेना-देना नहीं है ...? :-)
DevSolar

11
मैं पास -iकरने का सुझाव देता हूं tee। अन्यथा, सिग्नल इंटरप्ट (जाल) मुख्य स्क्रिप्ट में स्टडआउट को बाधित करेगा। उदाहरण के लिए, यदि आपके पास एक trap 'echo foo' EXITऔर फिर प्रेस है ctrl+c, तो आप " फू " नहीं देखेंगे । इसलिए मैं उत्तर को संशोधित करूंगा exec &> >(tee -ia file)
JamesThomasMoon1979

173

स्वीकृत उत्तर STDERR को एक अलग फ़ाइल डिस्क्रिप्टर के रूप में संरक्षित नहीं करता है। इसका मत

./script.sh >/dev/null

barटर्मिनल पर आउटपुट नहीं करेगा , केवल लॉगफाइल और

./script.sh 2>/dev/null

इच्छा उत्पादन दोनों fooऔर barटर्मिनल के लिए। स्पष्ट रूप से यह वह व्यवहार नहीं है जिसकी अपेक्षा एक सामान्य उपयोगकर्ता कर सकता है। यह एक ही लॉग फ़ाइल में संलग्न दोनों अलग-अलग टी प्रक्रियाओं का उपयोग करके तय किया जा सकता है:

#!/bin/bash

# See (and upvote) the comment by JamesThomasMoon1979 
# explaining the use of the -i option to tee.
exec >  >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)

echo "foo"
echo "bar" >&2

(ध्यान दें कि ऊपर शुरू में लॉग फ़ाइल को छोटा नहीं करता है - यदि आप चाहते हैं कि व्यवहार आपको जोड़ना चाहिए

>foo.log

स्क्रिप्ट के शीर्ष पर।)

की POSIX.1-2008 विनिर्देशtee(1) की आवश्यकता है कि उत्पादन unbuffered है, यानी नहीं भी लाइन बफ़र, इसलिए इस मामले यह संभव है कि STDOUT और STDERR की एक ही लाइन पर खत्म हो सकता है में foo.log; हालाँकि, यह टर्मिनल पर भी हो सकता है, इसलिए लॉग फ़ाइल टर्मिनल पर देखी जा सकने वाली चीज़ों का एक वफादार प्रतिबिंब होगी , अगर इसका सटीक दर्पण नहीं है। यदि आप चाहते हैं कि STDOUT लाइनें साफ तौर पर STDERR लाइनों से अलग हो जाएं, तो दो लॉग फ़ाइलों का उपयोग करने पर विचार करें, संभवतः कालानुक्रमिक पुनर्मूल्यांकन की अनुमति देने के लिए प्रत्येक पंक्ति पर दिनांक स्टाम्प उपसर्गों के साथ।


किसी कारण से, मेरे मामले में, जब स्क्रिप्ट को सी-प्रोग्राम सिस्टम () कॉल से निष्पादित किया जाता है, तो दो टी उप-प्रक्रियाएं मुख्य स्क्रिप्ट के बाहर निकलने के बाद भी मौजूद रहती हैं। इसलिए मुझे इस तरह से जाल जोड़ना था:exec > >(tee -a $LOG) trap "kill -9 $! 2>/dev/null" EXIT exec 2> >(tee -a $LOG >&2) trap "kill -9 $! 2>/dev/null" EXIT
अलावे

15
मैं पास -iकरने का सुझाव देता हूं tee। अन्यथा, सिग्नल इंटरप्ट (जाल) स्क्रिप्ट में स्टडआउट को बाधित करेगा। उदाहरण के लिए, यदि आप trap 'echo foo' EXITऔर फिर दबाते हैं ctrl+c, तो आप " फू " नहीं देखेंगे । इसलिए मैं उत्तर को संशोधित करूंगा exec > >(tee -ia foo.log)
जेम्सथोमसून 1979

मैंने इसके आधार पर कुछ छोटी "खट्टा" स्क्रिप्ट बनाई। उन्हें स्क्रिप्ट में उपयोग कर सकते हैं जैसे . logया . log foo.log: sam.nipl.net/sh/log sam.nipl.net/sh/log-a
Sam Watkins

1
इस पद्धति के साथ समस्या यह है कि संदेश STDOUTपहले एक बैच के रूप में दिखाई देने वाले हैं, और फिर संदेश STDERRदिखाई देने वाले हैं। वे आम तौर पर अपेक्षित नहीं हैं।
CMCDragonkai 8

28

बिजीबॉक्स, मैकओएस बैश और नॉन-बैश शेल के लिए समाधान

स्वीकार किए गए जवाब निश्चित रूप से बैश के लिए सबसे अच्छा विकल्प है। मैं bash की पहुंच के बिना एक बिजीबॉक्स वातावरण में काम कर रहा हूं, और यह exec > >(tee log.txt)सिंटैक्स को नहीं समझता है । यह भी 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 with stdout 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, which is 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

यह एकमात्र उपाय है जो मैंने अब तक देखा है जो मैक पर काम करता है
माइक बग्लियो जूनियर

19

अपनी स्क्रिप्ट फ़ाइल के अंदर, इस तरह से सभी कोष्ठक के भीतर कमांड रखें:

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

5
वंशानुगत रूप से, ब्रेसिज़ का उपयोग भी कर सकते हैं ( {})
ग्लेन जैकमैन

अच्छी तरह से हाँ, मैंने माना कि, लेकिन यह वर्तमान शेल स्टडआउट का पुनर्निर्देशन नहीं है, इसका एक प्रकार का धोखा है, आप वास्तव में एक उपधारा चला रहे हैं और उस पर एक नियमित रूप से पिपर पुनर्निर्देशन कर रहे हैं। सोचा काम करता है। मैं इस और "पूंछ -f foo.log और" समाधान के साथ विभाजित हूं। एक बेहतर एक सतहों हो सकता है देखने के लिए थोड़ा इंतजार करेंगे। शायद नहीं जा रहा है;)
विटाली कुशनर

8
{} वर्तमान शेल वातावरण में एक सूची निष्पादित करता है। () एक उप-वातावरण में एक सूची निष्पादित करता है।

अरे नहीं। धन्यवाद। वहाँ स्वीकार किए गए उत्तर ने मेरे लिए काम नहीं किया, एक विंडोज़ सिस्टम पर मिंगडब्ल्यू के तहत चलने के लिए एक स्क्रिप्ट शेड्यूल करने की कोशिश की। इसने शिकायत की, मेरा मानना ​​है कि अनिमित प्रक्रिया प्रतिस्थापन के बारे में। इस उत्तर ने ठीक काम किया, निम्नलिखित बदलाव के बाद, दोनों stderr और stdout पर कब्जा करने के लिए: `` `-) | tee foo.log +) 2> और 1 | टी-फू.लॉग
जॉन कार्टर

14

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

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

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

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

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

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


10

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

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

# Rest of my script

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

ध्यान दें कि 'निष्पादित और>' स्टडआउट और स्टडर दोनों को रीडायरेक्ट करता है, यदि हम चाहें तो हम अलग से रीडायरेक्ट कर सकते हैं या अगर हम चाहें तो 'एग्जीक्यूटिव' में बदल सकते हैं।

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


डेविड ज़ेड के दूसरे विचार के समान उत्तर । इसकी टिप्पणियों पर एक नज़र डालें। +1 ;-)
ओलीब्रे

अच्छा काम करता है। मैं इसका $logfileहिस्सा नहीं समझ रहा हूं tee < ${logfile}.pipe $logfile &। विशेष रूप से, मैंने इसे पूर्ण विस्तारित कमांड लॉग लाइनों (से set -x) को फाइल करने के लिए बदलने की कोशिश की, जबकि केवल '+' को बिना लीडरशिप के बदलने के लिए बदलकर, (tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &लेकिन संबंधित एक त्रुटि संदेश प्राप्त किया $logfile। क्या आप teeलाइन को थोड़ा और विस्तार से समझा सकते हैं ?
क्रिस जॉनसन

मैंने इसका परीक्षण किया और ऐसा लगता है कि यह उत्तर STDERR को संरक्षित नहीं करता है (इसे STDOUT में मिला दिया गया है), इसलिए यदि आप त्रुटि का पता लगाने या अन्य पुनर्निर्देशन के लिए अलग-अलग धाराओं पर भरोसा करते हैं, तो आपको एडम के उत्तर को देखना चाहिए।
HeroCC


1

यह नहीं कह सकता कि मैं निष्पादन पर आधारित किसी भी समाधान के साथ सहज हूं। मैं सीधे टी का उपयोग करना पसंद करता हूं, इसलिए अनुरोध किए जाने पर मैं स्क्रिप्ट को टी के साथ कॉल करता हूं:

# my script: 

check_tee_output()
{
    # copy (append) stdout and stderr to log file if TEE is unset or true
    if [[ -z $TEE || "$TEE" == true ]]; then 
        echo '-------------------------------------------' >> log.txt
        echo '***' $(date) $0 $@ >> log.txt
        TEE=false $0 $@ 2>&1 | tee --append log.txt
        exit $?
    fi 
}

check_tee_output $@

rest of my script

यह आपको ऐसा करने की अनुमति देता है:

your_script.sh args           # tee 
TEE=true your_script.sh args  # tee 
TEE=false your_script.sh args # don't tee
export TEE=false
your_script.sh args           # tee

आप इसे कस्टमाइज़ कर सकते हैं, जैसे कि टी = गलत को डिफ़ॉल्ट बनाएं, इसके बजाय टीईई को लॉग फ़ाइल को दबाए रखें, आदि मुझे लगता है कि यह समाधान जंबो के समान है, लेकिन सरल, शायद मेरी सीमाएं हैं जो मैं अभी तक नहीं आया हूं।


-1

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

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इसे मूल शेल में बाद में छोड़ दें)।


1
टेल एक रनिंग प्रक्रिया को पीछे छोड़ देगा, दूसरी स्क्रिप्ट टी में ब्लॉक हो जाएगी, या आपको इसे उसी के साथ चलाने की आवश्यकता होगी और जिस स्थिति में यह पहली बार में प्रक्रिया को छोड़ देगा।
विटाली कुशनर

@ वैवाहिक: उफ़, पृष्ठभूमि को भूल गया tee- मैंने संपादित किया है। जैसा कि मैंने कहा, न तो एक सही समाधान है, लेकिन जब उनके माता-पिता शेल समाप्त हो जाएंगे, तो पृष्ठभूमि की प्रक्रियाएं समाप्त हो जाएंगी, इसलिए आपको संसाधनों के बारे में हमेशा के लिए चिंता करने की ज़रूरत नहीं है।
डेविड जेड

1
Yikes: ये आकर्षक लग रहे हैं, लेकिन पूंछ -f का उत्पादन भी foo.log करने जा रहा है। आप निष्पादन से पहले पूंछ -f चलाकर इसे ठीक कर सकते हैं, लेकिन माता-पिता के समाप्त होने के बाद भी पूंछ को चलाना बाकी है। आपको इसे स्पष्ट रूप से मारने की जरूरत है, शायद एक जाल में 0.
विलियम पर्ससेल

यीप। यदि स्क्रिप्ट को पृष्ठभूमि में रखा गया है, तो यह सभी प्रक्रियाओं को छोड़ देता है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.