टर्मिनल और लॉग फ़ाइल में जाने के लिए मुझे STDOUT और STDERR दोनों कैसे मिलेंगे?


104

मेरे पास एक स्क्रिप्ट है जो गैर-तकनीकी उपयोगकर्ताओं द्वारा अंतःक्रियात्मक रूप से चलाई जाएगी। स्क्रिप्ट STDOUT में स्थिति अपडेट लिखती है ताकि उपयोगकर्ता यह सुनिश्चित कर सके कि स्क्रिप्ट ठीक चल रही है।

मैं चाहता हूँ कि STDOUT और STDERR दोनों ही टर्मिनल पर पुनर्निर्देशित हो जाएँ (ताकि उपयोगकर्ता यह देख सके कि स्क्रिप्ट काम कर रही है और साथ ही देखें कि क्या समस्या थी)। मैं यह भी चाहता हूं कि दोनों धाराएं एक लॉग फ़ाइल में पुनर्निर्देशित हो जाएं।

मैंने नेट पर समाधानों का एक गुच्छा देखा है। कुछ काम नहीं करते हैं और अन्य बुरी तरह से जटिल हैं। मैंने एक व्यावहारिक समाधान विकसित किया है (जो मैं एक उत्तर के रूप में दर्ज करूंगा), लेकिन यह हास्यास्पद है।

सही समाधान कोड की एक एकल पंक्ति होगी जिसे किसी भी स्क्रिप्ट की शुरुआत में शामिल किया जा सकता है जो टर्मिनल और लॉग फ़ाइल दोनों धाराओं को भेजता है।

EDIT: STDERR को STDOUT में रीडायरेक्ट करना और परिणाम को टी करने के लिए पाइप करना, लेकिन यह उपयोगकर्ताओं पर निर्भर करता है कि वे आउटपुट को रीडायरेक्ट और पाइप करना याद रखें। मैं चाहता हूं कि लॉगिंग फुल-प्रूफ और ऑटोमैटिक हो (यही वजह है कि मैं स्क्रिप्ट में ही समाधान को एम्बेड करने में सक्षम होना चाहता हूं।)


अन्य पाठकों के लिए: इसी तरह के सवाल: stackoverflow.com/questions/692000/…
pevik

1
मुझे गुस्सा आ रहा है कि @JasonSydes को छोड़कर हर कोई (मेरे सहित!) पटरी से उतर गया और एक अलग सवाल का जवाब दिया। और जेसन का जवाब अविश्वसनीय है, जैसा कि मैंने टिप्पणी की थी। मैं आपके द्वारा पूछे गए प्रश्न (और आपके EDIT में जोर दिया गया) के लिए एक वास्तविक विश्वसनीय उत्तर देखना पसंद करूंगा।
डॉन हैच

ओह रुको, मैं इसे वापस लेता हूं। @PaTTromblin का स्वीकृत उत्तर इसका उत्तर देता है। मैं इसमें बहुत दूर तक नहीं पढ़ा था।
डॉन हैच

जवाबों:


167

एक फ़ाइल और स्क्रीन पर पुनर्निर्देशित करने के लिए "टी" का उपयोग करें। आपके द्वारा उपयोग किए जाने वाले शेल के आधार पर, आपको सबसे पहले stdout का उपयोग करने के लिए stderr को पुनर्निर्देशित करना होगा

./a.out 2>&1 | tee output

या

./a.out |& tee output

Csh में, "स्क्रिप्ट" नामक एक अंतर्निहित कमांड है जो स्क्रीन पर एक फ़ाइल में जाने वाली हर चीज को कैप्चर करेगा। आप इसे "स्क्रिप्ट" लिखकर शुरू करते हैं, फिर आप जो भी करना चाहते हैं उसे कैप्चर करें, फिर स्क्रिप्ट फ़ाइल को बंद करने के लिए कंट्रोल-डी को हिट करें। मुझे श / बश / क्ष के लिए एक समकक्ष का पता नहीं है।

इसके अलावा, चूंकि आपने संकेत दिया है कि ये आपकी अपनी स्क्रिप्ट हैं जिन्हें आप संशोधित कर सकते हैं, तो आप पूरी स्क्रिप्ट को ब्रेसिज़ या कोष्ठक के साथ आंतरिक रूप से पुनर्निर्देशित कर सकते हैं, जैसे

  #!/bin/sh
  {
    ... whatever you had in your script before
  } 2>&1 | tee output.file

4
मुझे नहीं पता था कि आप शेल स्क्रिप्ट में कमांड को ब्रैकेट कर सकते हैं। दिलचस्प।
जेमी

1
मैं ब्रैकेट शॉर्टकट की भी सराहना करता हूं! किसी कारण के लिए, 2>&1 | tee -a filenameअपनी स्क्रिप्ट से फ़ाइल में स्टेडर को नहीं बचा रहा था, लेकिन जब मैंने कमांड को कॉपी किया और इसे टर्मिनल में चिपकाया तो यह ठीक काम कर गया! ब्रैकेट चाल ठीक काम करता है, यद्यपि।
एड ब्रनिन

8
ध्यान दें कि stdout और stderr के बीच का अंतर खो जाएगा, क्योंकि teeout सब कुछ को प्रिंट करता है।
फ़्लिम

2
FYI करें: 'स्क्रिप्ट' कमांड अधिकांश वितरणों में उपलब्ध है (यह उपयोग-
लिनेक्स

2
@ फाल्म, क्या स्टडआउट और स्टडर के बीच अंतर बनाए रखने के लिए कोई रास्ता (कोई अन्य तरीका) है?
गेब्रियल

20

करीब डेढ़ दशक बाद मंजूर ...

मेरा मानना ​​है कि यह ओपी द्वारा मांगी गई "सही समाधान" है।

यहाँ एक लाइनर है जिसे आप अपनी बैश स्क्रिप्ट के शीर्ष पर जोड़ सकते हैं:

exec > >(tee -a $HOME/logfile) 2>&1

यहाँ एक छोटी स्क्रिप्ट है जो इसके उपयोग को प्रदर्शित करती है:

#!/usr/bin/env bash

exec > >(tee -a $HOME/logfile) 2>&1

# Test redirection of STDOUT
echo test_stdout

# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist

(नोट: यह केवल बैश के साथ काम करता है। यह / बिन / श के साथ काम नहीं करेगा ।)

यहाँ से अपनाया गया ; मूल नहीं था, जो मैं बता सकता हूं, लॉगफ़ाइल में एसटीडीआरआर को पकड़ो। यहां से एक नोट के साथ फिक्स्ड ।


3
ध्यान दें कि stdout और stderr के बीच का अंतर खो जाएगा, क्योंकि teeout सब कुछ को प्रिंट करता है।
फ्लिम्म

@ क्लीमर स्टॉर्मर को अलग-अलग टी प्रक्रिया में पुनर्निर्देशित किया जा सकता है, जिसे फिर से स्टडर पर पुनर्निर्देशित किया जा सकता है।
जारो

@ फ़ीलम, मैंने यहाँ जारो का सुझाव लिखा: stackoverflow.com/a/53051506/1054322
MatrixManAtYrService

1
यह समाधान, अब तक प्रस्तावित अधिकांश अन्य समाधानों की तरह, रेस-प्रोन है। यानी, जब वर्तमान स्क्रिप्ट पूरी हो जाती है और वापस आ जाती है, या तो उपयोगकर्ता के प्रॉम्प्ट या कुछ उच्च-स्तरीय कॉलिंग स्क्रिप्ट के लिए, टी, जो पृष्ठभूमि में चल रही है, अभी भी चल रही होगी, और स्क्रीन पर अंतिम कुछ पंक्तियों का उत्सर्जन कर सकती है और करने के लिए लॉगफाइल लेट (यानी, प्रॉम्प्ट के बाद स्क्रीन पर, और लॉगफाइल के बाद लॉगफाइल पूरा होने की उम्मीद है)।
डॉन हैच

1
हालांकि, यह अब तक प्रस्तावित एकमात्र उत्तर है जो वास्तव में प्रश्न को संबोधित करता है!
डॉन हैच

9

पैटर्न

the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )

यह दोनों अलग-अलग और stderr को पुनर्निर्देशित करता है, और यह कॉलर को stdout और stderr की अलग-अलग प्रतियां भेजता है (जो कि आपका टर्मिनल हो सकता है)।

  • जब तक teeएस समाप्त नहीं हो जाता है , तब तक यह अगले बयान पर आगे नहीं बढ़ेगा ।

  • बैश में, आप पा सकते हैं कि जो भी स्टेटमेंट आता है उसके बाद आउटपुट की अंतिम कुछ लाइनें दिखाई देती हैं ।

किसी भी स्थिति में, सही बिट सही स्थानों पर जाते हैं।


व्याख्या

यहाँ एक स्क्रिप्ट है (./example में संग्रहीत):

#! /usr/bin/env bash
the_cmd()
{
    echo out;
    1>&2 echo err;
}

the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )

यहाँ एक सत्र है:

$ foo=$(./example)
    err

$ echo $foo
    out

$ cat stdout.txt
    out

$ cat stderr.txt
    err

यहां देखिए यह कैसे काम करता है:

  1. दोनों teeप्रक्रियाएं शुरू की जाती हैं, उनके stdins को डिस्क्रिप्टर फाइल करने के लिए सौंपा जाता है। क्योंकि वे प्रक्रिया प्रतिस्थापन में संलग्न हैं , उन फ़ाइल डिस्क्रिप्टर के पथ को कॉलिंग कमांड में प्रतिस्थापित किया जाता है, इसलिए अब यह कुछ इस तरह दिखता है:

the_cmd 1> /proc/self/fd/13 2> /proc/self/fd/14

  1. the_cmd रन, पहली फाइल डिस्क्रिप्टर पर stdout लिखना, और दूसरे के लिए stderr।

  2. बैश मामले में, एक बार the_cmdखत्म हो जाने के बाद, निम्न कथन तुरंत होता है (यदि आपका टर्मिनल कॉलर है, तो आप अपने प्रॉम्प्ट दिखाई देंगे)।

  3. Zsh मामले में, एक बार the_cmdखत्म होने के बाद, शेल teeआगे बढ़ने से पहले दोनों प्रक्रियाओं के समाप्त होने की प्रतीक्षा करता है । यहाँ इस पर अधिक ।

  4. पहली teeप्रक्रिया, जो the_cmdस्टडआउट से पढ़ रही है, कॉल करने वाले को उस स्टडआउट की एक प्रति वापस लिखती है क्योंकि वही teeकरता है। इसके आउटपुट को पुनर्निर्देशित नहीं किया जाता है, इसलिए वे इसे वापस कॉलर को अपरिवर्तित कर देते हैं

  5. दूसरी teeप्रक्रिया ने इसे stdoutकॉलर को पुनर्निर्देशित किया है stderr(जो अच्छा है, क्योंकि यह स्टैडर the_cmd's stderr' से पढ़ रहा है )। इसलिए जब यह अपने स्टडआउट को लिखता है, तो वे बिट्स कॉलर के स्टडर पर जाते हैं।

यह stderr को फाइलों में और कमांड के आउटपुट में दोनों से अलग रखता है।

यदि पहला टी किसी भी त्रुटि को लिखता है, तो वे दोनों stderr फ़ाइल में और कमांड के stderr में दिखाएगा, यदि दूसरा टी किसी भी त्रुटि को लिखता है, तो वे केवल टर्मिनल के stderr में दिखाई देंगे।


यह वास्तव में उपयोगी है और मुझे क्या चाहिए। हालांकि मुझे यकीन नहीं है कि विंडोज बैच स्क्रिप्ट में कोष्ठक (पहली पंक्ति में दिखाया गया है) के उपयोग को कैसे दोहराया जाए। ( teeविचाराधीन प्रणाली पर उपलब्ध है।) मुझे जो त्रुटि मिलती है वह है "प्रक्रिया फ़ाइल तक नहीं पहुंच सकती है क्योंकि इसका उपयोग किसी अन्य प्रक्रिया द्वारा किया जा रहा है।"
एजी हैमरथिफ

यह समाधान, अब तक प्रस्तावित अधिकांश अन्य समाधानों की तरह, रेस-प्रोन है। यानी, जब वर्तमान स्क्रिप्ट पूरी हो जाती है और वापस आ जाती है, या तो उपयोगकर्ता के प्रॉम्प्ट या कुछ उच्च-स्तरीय कॉलिंग स्क्रिप्ट के लिए, टी, जो पृष्ठभूमि में चल रही है, अभी भी चल रही होगी, और स्क्रीन पर अंतिम कुछ पंक्तियों का उत्सर्जन कर सकती है और करने के लिए लॉगफाइल लेट (यानी, प्रॉम्प्ट के बाद स्क्रीन पर, और लॉगफाइल के बाद लॉगफाइल पूरा होने की उम्मीद है)।
डॉन हैच

2
@DonHatch क्या आप इस समस्या के समाधान का प्रस्ताव रख सकते हैं?
पाइलिप

मुझे एक टेस्ट मामले में भी दिलचस्पी होगी जो दौड़ को स्पष्ट करता है। ऐसा नहीं है कि मुझे संदेह है, लेकिन इससे बचने का प्रयास करना कठिन है क्योंकि मैंने ऐसा नहीं देखा है।
MatrixManAtYrService

@ पाइलिप मेरे पास कोई हल नहीं है। मुझे एक में बहुत दिलचस्पी होगी ।
डॉन हैच

4

Stdout को अपने आदेश में पुनर्निर्देशित करने के लिए stderr को पुनर्निर्देशित करें: 2>&1 टर्मिनल पर आउटपुट करने और फ़ाइल में लॉग इन करने के लिए जिसे आपको उपयोग करना चाहिएtee

दोनों एक साथ दिखेंगे:

 mycommand 2>&1 | tee mylogfile.log

संपादित करें: अपनी स्क्रिप्ट में एम्बेड करने के लिए आप ऐसा ही करेंगे। तो आपकी स्क्रिप्ट

#!/bin/sh
whatever1
whatever2
...
whatever3

के रूप में समाप्त होगा

#!/bin/sh
( whatever1
whatever2
...
whatever3 ) 2>&1 | tee mylogfile.log

2
ध्यान दें कि stdout और stderr के बीच का अंतर खो जाएगा, क्योंकि teeout सब कुछ को प्रिंट करता है।
फ्लिम्म

4

संपादित करें: मैं देख रहा हूँ कि मैं पटरी से उतर गया और एक अलग सवाल का जवाब देने से समाप्त हो गया। असली सवाल का जवाब पॉल टॉम्बलिन के जवाब में सबसे नीचे है। (यदि आप किसी कारण के लिए अलग-अलग stdout और stderr को पुनर्निर्देशित करने के लिए उस समाधान को बढ़ाना चाहते हैं, तो आप मेरे द्वारा बताई गई तकनीक का उपयोग कर सकते हैं।)


मैं एक जवाब चाह रहा हूँ जो stdout और stderr के बीच अंतर को बरकरार रखता है। दुर्भाग्य से अब तक दिए गए सभी उत्तर जो यह बताते हैं कि भेद जाति-प्रवण हैं: वे अधूरे इनपुट को देखते हुए कार्यक्रमों को जोखिम में डालते हैं, जैसा कि मैंने टिप्पणियों में बताया है।

मुझे लगता है कि मुझे अंत में एक उत्तर मिला है जो भेद को संरक्षित करता है, दौड़ प्रवण नहीं है, और बहुत बुरी तरह से भी नहीं है।

पहला बिल्डिंग ब्लॉक: स्टैडआउट और स्टैडर को स्वैप करने के लिए:

my_command 3>&1 1>&2 2>&3-

दूसरा बिल्डिंग ब्लॉक: यदि हम केवल stderr को फ़िल्टर करना चाहते हैं (जैसे टी), तो हम इसे पूरा कर सकते हैं कि stdout और stderr, फ़िल्टरिंग और फिर स्वैपिंग करके:

{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-

अब बाकी आसान है: हम शुरुआत में या तो एक स्टडआउट फ़िल्टर जोड़ सकते हैं:

{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-

या अंत में:

{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter

अपने आप को यह समझाने के लिए कि उपरोक्त दोनों कमांड काम करते हैं, मैंने निम्नलिखित का उपयोग किया:

alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'

आउटपुट है:

...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr

और मेरा संकेत " teed stderr: to stderr" के तुरंत बाद वापस आता है , जैसा कि अपेक्षित था।

पाद के बारे में zsh :

उपरोक्त समाधान बाश में काम करता है (और शायद कुछ अन्य गोले, मुझे यकीन नहीं है), लेकिन यह zsh में काम नहीं करता है। दो कारण हैं कि यह zsh में विफल रहता है:

  1. वाक्य-विन्यास 2>&3-को zsh द्वारा नहीं समझा जाता है; इसे फिर से लिखना होगा2>&3 3>&-
  2. zsh में (अन्य गोले के विपरीत), यदि आप किसी फ़ाइल डिस्क्रिप्टर को पहले से ही खोल चुके हैं, तो कुछ मामलों में (मुझे पूरी तरह से समझ में नहीं आता कि यह कैसे तय होता है) इसके बजाय एक अंतर्निहित टी-लाइक व्यवहार करता है। इससे बचने के लिए, आपको इसे रीडायरेक्ट करने से पहले प्रत्येक fd को बंद करना होगा।

इसलिए, उदाहरण के लिए, मेरे दूसरे समाधान को zsh के रूप में फिर से लिखना होगा {my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter(जो कि बहुत काम करता है, लेकिन भयानक रूप से क्रिया है )।

दूसरी ओर, आप zsh के लिए बहुत छोटा समाधान पाने के लिए zsh के रहस्यमय अंतर्निहित अंतर्निहित टीज़िंग का लाभ उठा सकते हैं, जो टी को बिल्कुल भी नहीं चलाता है:

my_command >&1 >stdout.txt 2>&2 2>stderr.txt

(मुझे लगता है कि मुझे पता चला है कि डॉक्स से अनुमान नहीं लगाया गया है >&1और 2>&2वह चीज है जो zsh की अंतर्निहित टीज़िंग को ट्रिगर करती है; मैंने पाया कि ट्रायल-एंड-एरर के द्वारा।)


मैं इसके साथ बैश में खेला और यह अच्छी तरह से काम करता है। अनुकूलता ग्रहण करने की आदत वाले zsh उपयोगकर्ताओं के लिए बस एक चेतावनी (अपने आप की तरह), यह वहां अलग तरह से व्यवहार करता है: gist.github.com/MatrixManAtYrService/…
MatrixManAtYrService

@MatrixManAtYrService मेरा मानना ​​है कि मुझे zsh स्थिति पर एक हैंडल मिला है, और यह पता चला है कि zsh में एक बहुत अधिक नट समाधान है। मेरा संपादन देखें "zsh के बारे में फुटनोट"।
डॉन हैच

इस तरह के विस्तार से समाधान समझाने के लिए धन्यवाद। क्या आप यह भी जानते हैं कि my_functionनेस्टेड स्टडआउट / स्टेडर फ़िल्टरिंग में फ़ंक्शन ( ) का उपयोग करते समय रिटर्न कोड कैसे प्राप्त करें ? मैंने किया { { my_function || touch failed;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filterलेकिन असफलता के सूचक के रूप में एक फ़ाइल बनाने के लिए अजीब लगता है ...
पाइलिप

@ पाइलिप मैं अपमान नहीं करता। आप पूछ सकते हैं कि एक अलग प्रश्न के रूप में (शायद एक सरल पाइपलाइन के साथ)।
डॉन हैच

2

scriptअपनी स्क्रिप्ट में कमांड का उपयोग करें (आदमी 1 स्क्रिप्ट)

एक आवरण शेलस्क्रिप्ट (2 लाइनें) बनाएं जो स्क्रिप्ट सेट करता है () और फिर कॉल आउट।

भाग 1: लपेटो। श

#!/bin/sh
script -c './realscript.sh'
exit

भाग 2: realscript.sh

#!/bin/sh
echo 'Output'

परिणाम:

~: sh wrap.sh 
Script started, file is typescript
Output
Script done, file is typescript
~: cat typescript 
Script started on fr. 12. des. 2008 kl. 18.07 +0100
Output

Script done on fr. 12. des. 2008 kl. 18.07 +0100
~:

1

टी कार्यक्रम का उपयोग करें और स्टडआउट करने के लिए डंप स्ट्रैडर का उपयोग करें।

 program 2>&1 | tee > logfile

1

मैंने "RunScript.sh" नामक एक स्क्रिप्ट बनाई। इस स्क्रिप्ट की सामग्री है:

${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log

मैं इसे इस तरह से कॉल करता हूं:

./RunScript.sh ScriptToRun Param1 Param2 Param3 ...

यह काम करता है, लेकिन इसके लिए बाहरी स्क्रिप्ट के माध्यम से एप्लिकेशन की स्क्रिप्ट को चलाना पड़ता है। यह थोड़ा टेढ़ा है।


9
आप $ 1 $ 2 $ 3 के साथ व्हॉट्सएप युक्त तर्कों का समूह खो देंगे ... , आपको (w / उद्धरण) का उपयोग करना चाहिए: "$ @"
NVRAM

1

एक साल बाद, यहां कुछ भी लॉग करने के लिए एक पुरानी बैश स्क्रिप्ट है। उदाहरण के लिए,
teelog make ...एक लॉग किए गए लॉग नाम पर लॉग इन करता है (और नेस्टेड नेस्ट के लिए makeभी ट्रिक देखें ।)

#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"

Help() {
cat <<!

    $me anycommand args ...

logs the output of "anycommand ..." as well as displaying it on the screen,
by running
    anycommand args ... 2>&1 | tee `day`-command-args.log

That is, stdout and stderr go to both the screen, and to a log file.
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out;
see http://en.wikipedia.org/wiki/Tee_(command) ).

The default log file name is made up from "command" and all the "args":
    $me cmd -opt dir/file  logs to `day`-cmd--opt-file.log .
To log to xx.log instead, either export log=xx.log or
    $me log=xx.log cmd ...
If "logdir" is set, logs are put in that directory, which must exist.
An old xx.log is moved to /tmp/\$USER-xx.log .

The log file has a header like
    # from: command args ...
    # run: date pwd etc.
to show what was run; see "From" in this file.

Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file:
    command args ... > `day`-command-args.log
and tees stderr to both the log file and the terminal -- bash only.

Some commands that prompt for input from the console, such as a password,
don't prompt if they "| tee"; you can only type ahead, carefully.

To log all "make" s, including nested ones like
    cd dir1; \$(MAKE)
    cd dir2; \$(MAKE)
    ...
export MAKE="$me make"

!
  # See also: output logging in screen(1).
    exit 1
}


#-------------------------------------------------------------------------------
# bzutil.sh  denisbz may2008 --

day() {  # 30mar, 3mar
    /bin/date +%e%h  |  tr '[A-Z]' '[a-z]'  |  tr -d ' '
}

edate() {  # 19 May 2008 15:56
    echo `/bin/date "+%e %h %Y %H:%M"`
}

From() {  # header  # from: $*  # run: date pwd ...
    case `uname` in Darwin )
        mac=" mac `sw_vers -productVersion`"
    esac
    cut -c -200 <<!
${comment-#} from: $@
${comment-#} run: `edate`  in $PWD `uname -n` $mac `arch` 

!
    # mac $PWD is pwd -L not -P real
}

    # log name: day-args*.log, change this if you like --
logfilename() {
    log=`day`
    [[ $1 == "sudo" ]]  &&  shift
    for arg
    do
        log="$log-${arg##*/}"  # basename
        (( ${#log} >= 100 ))  &&  break  # max len 100
    done
            # no blanks etc in logfilename please, tr them to "-"
    echo $logdir/` echo "$log".log  |  tr -C '.:+=[:alnum:]_\n' - `
}

#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
    echo "$0 version: $Version"
    exit 1 ;;
"" | -* )
    Help
esac

    # scan log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
    export "$1"
    shift
done

: ${logdir=.}
[[ -w $logdir ]] || {
    echo >&2 "error: $me: can't write in logdir $logdir"
    exit 1
    }
: ${log=` logfilename "$@" `}
[[ -f $log ]]  &&
    /bin/mv "$log" "/tmp/$USER-${log##*/}"


case ${0##*/} in  # basename
log | Log )  # both to log, stderr to caller's stderr too --
{
    From "$@"
    "$@"
} > $log  2> >(tee /dev/stderr)  # bash only
    # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe
;;

* )
#-------------------------------------------------------------------------------
{
    From "$@"  # header: from ... date pwd etc.

    "$@"  2>&1  # run the cmd with stderr and stdout both to the log

} | tee $log
    # mac tee buffers stdout ?

esac

मुझे पता है कि इस तरह से टिप्पणी करने में देर हो रही है लेकिन मुझे इस स्क्रिप्ट के लिए धन्यवाद कहना होगा। बहुत उपयोगी और अच्छी तरह से प्रलेखित!
स्टीफन मिमी

धन्यवाद @stephenmm; यह कभी नहीं "उपयोगी" या "सुधार किया जा सकता" कहने के लिए बहुत देर हो चुकी।
डेसीस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.