पाइप के साथ "टी" का उपयोग करते हुए मैं एक फ़ाइल पर स्टडर्र कैसे लिखूं?


541

मुझे पता है कि टर्मिनल में teeआउटपुट प्रदर्शित करने के लिए कैसे ( STDOUT) का उपयोग aaa.shकरना है bbb.out, जबकि अभी भी इसे प्रदर्शित करना है:

./aaa.sh | tee bbb.out

अब मैं STDERRएक फ़ाइल को कैसे लिखूंगा ccc.out, जिसका नाम अभी भी प्रदर्शित है?


2
स्पष्ट करने के लिए - क्या आप स्क्रीन के साथ-साथ फाइल पर भी जाने के लिए स्ट्रैंडर चाहते हैं?
चार्ल्स डफी

मैंने किया, मैं अपनी पोस्ट को स्पष्ट करने के लिए संपादित करूंगा। मुझे विश्वास है कि लुनथ का समाधान पर्याप्त होगा। सभी को मदद के लिए धन्यवाद!
जपरानीच

जवाबों:


784

मैं मान रहा हूं कि आप अभी भी टर्मिनल पर STDERR और STDOUT देखना चाहते हैं। आप जोश केली के जवाब के लिए जा सकते हैं, लेकिन मुझे लगता tailहै कि बैकग्राउंड में जो कि आपके लॉग फाइल को बहुत ही हैकिश और क्लूडी के आउटपुट देता है। ध्यान दें कि आपको एक्सरा एफडी रखने की आवश्यकता है और इसे मारने के बाद सफाई करें और तकनीकी रूप से ऐसा करना चाहिए trap '...' EXIT

ऐसा करने का एक बेहतर तरीका है, और आप इसे पहले ही खोज चुके हैं tee:।

केवल, अपने स्टडआउट के लिए इसका उपयोग करने के बजाय, स्टडआउट के लिए टी और स्टडर के लिए एक है। आप इसे कैसे पूरा करेंगे? प्रक्रिया प्रतिस्थापन और फ़ाइल पुनर्निर्देशन:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

आइए इसे अलग करें और समझाएं:

> >(..)

>(...)(प्रक्रिया प्रतिस्थापन) एक फीफो बनाता है और teeउस पर सुनने देता है। फिर, यह FIFO >के STDOUT को पुनर्निर्देशित करने के लिए (फ़ाइल पुनर्निर्देशन) का उपयोग करता है commandजिसे आपका पहला teeसुन रहा है।

दूसरी के लिए एक ही बात:

2> >(tee -a stderr.log >&2)

हम teeएसटीडीआईएन से पढ़ी जाने वाली प्रक्रिया को बनाने के लिए फिर से प्रक्रिया प्रतिस्थापन का उपयोग करते हैं और इसमें डंप करते हैं stderr.logteeSTDOUT पर इसके इनपुट को वापस आउटपुट करता है, लेकिन चूंकि इसका इनपुट हमारा STDERR है, इसलिए हम teeअपने STDERR के बारे में फिर से अप्रत्यक्ष करना चाहते हैं। फिर हम फ़ाइल पुनर्निर्देशन का उपयोग commandFIFO के इनपुट ( tee'STDIN) में' STDERR ' को पुनर्निर्देशित करते हैं ।

Http://mywiki.wooledge.org/BashGuide/InputAndOutput देखें

प्रक्रिया प्रतिस्थापन उन बहुत ही प्यारी चीजों में से एक है जो आपको bashअपने शेल के रूप में चुनने के बोनस के रूप में मिलती है sh(पोसिक्स या बॉर्न) के विपरीत ।


में sh, आपको चीजों को मैन्युअल रूप से करना होगा:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

5
मैंने यह कोशिश की: $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)जो काम करता है, लेकिन इनपुट की प्रतीक्षा करता है। क्या ऐसा होने का एक सरल कारण है?
जस्टिन

1
@SillyFreak मुझे समझ नहीं आ रहा है कि आप क्या करना चाहते हैं या आपको क्या समस्या है। इको टेस्ट; बाहर निकलने से स्टडआउट पर कोई उत्पादन नहीं होता है, इसलिए गलती खाली रहेगी।
लहनाथ

1
उस टिप्पणी के लिए धन्यवाद; मुझे पता चला कि बाद में मेरी तार्किक त्रुटि क्या थी: जब एक इंटरैक्टिव शेल के रूप में आमंत्रित किया गया था, तो बैश एक कमांड प्रॉम्प्ट प्रिंट करता है और स्टेडर से बाहर निकलता है। हालांकि, यदि स्टैडर पुनर्निर्देशित किया जाता है, तो बैश डिफ़ॉल्ट रूप से गैर-सक्रिय हो जाता है; तुलना करें /bin/bash 2> errऔर/bin/bash -i 2> err
मूर्ख सनकी

15
और जो लोग "देखकर विश्वास कर रहे हैं", उनके लिए एक त्वरित परीक्षा:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
मैथ्यू विलकॉक्सन

2
वाह ! अच्छा जवाब: "आइए इसे अलग करें और समझाएं" +1
जलानब

672

बस क्यों नहीं:

./aaa.sh 2>&1 | tee -a log

यह बस लॉग इन करने और स्क्रीन करने के लिए दोनों को echoes रीडायरेक्ट stderrकरता है stdout। शायद मुझे कुछ याद आ रहा है, क्योंकि कुछ अन्य समाधान वास्तव में जटिल लगते हैं।

नोट: बैश संस्करण 4 के बाद से आप इसके लिए |&एक संक्षिप्त नाम के रूप में उपयोग कर सकते हैं 2>&1 |:

./aaa.sh |& tee -a log

85
यदि आप दोनों stdout (चैनल 1) और stderr (चैनल 2) एक ही फ़ाइल (stdout और sterr दोनों के मिश्रण वाली एकल फ़ाइल) में लॉग इन करना चाहते हैं, तो यह ठीक काम करता है। अन्य, अधिक जटिल समाधान आपको stdout और stderr को 2 अलग-अलग फ़ाइलों (क्रमशः stdout.log और stderr.log) में अलग करने की अनुमति देता है। कभी-कभी यह महत्वपूर्ण है, कभी-कभी यह नहीं है।
टायलर रिक

19
अन्य समाधान कई मामलों में आवश्यक से कहीं अधिक जटिल हैं। यह मेरे लिए पूरी तरह से काम करता है।
dk विटामिन

13
इस पद्धति के साथ समस्या यह है कि आप एएए.एचएस प्रक्रिया से निकास / स्थिति कोड खो देते हैं, जो महत्वपूर्ण हो सकता है (जैसे कि मेकफाइल में उपयोग करते समय)। आपके पास स्वीकृत उत्तर के साथ यह समस्या नहीं है।
स्टेफन

9
यदि आप stdout / stderr को मर्ज नहीं करते हैं तो ./aaa.sh |& tee aaa.logकाम करता है (bash में)।
6

5
@Stefaan मेरा मानना ​​है कि यदि आप कमांड चेन को आगे set -o pipefailपीछे करते हैं ;या &&अगर मुझसे गलती नहीं हुई है तो आप बाहर निकलने की स्थिति को बरकरार रख सकते हैं ।
डेविड

59

यह Google के माध्यम से इसे खोजने वाले लोगों के लिए उपयोगी हो सकता है। बस उस उदाहरण को अनइंस्टॉल करें जिसे आप आज़माना चाहते हैं। बेशक, आउटपुट फ़ाइलों का नाम बदलने के लिए स्वतंत्र महसूस करें।

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}

6
नहीं, और मुझे लगता है कि निष्पादन कुछ समझाने का उपयोग कर सकता है। exec >का अर्थ है, एक फ़ाइल डिस्क्रिप्टर के लक्ष्य को एक निश्चित गंतव्य पर ले जाना। डिफ़ॉल्ट 1 है, इसलिए, exec > /dev/nullइस सत्र में अब से stdout के आउटपुट को / dev / null पर ले जाता है। इस सत्र के लिए वर्तमान फ़ाइल डिस्क्रिप्टर को करके देखा जा सकता है ls -l /dev/fd/। कोशिश करो! फिर देखें कि जब आप जारी करते हैं तो क्या होता है exec 2>/tmp/stderr.log., exec 3>&1इसका मतलब है, संख्या 3 के साथ एक नया फ़ाइल डिस्क्रिप्टर बनाएं, और इसे फ़ाइल डिस्क्रिप्टर के लक्ष्य पर पुनर्निर्देशित करें। 1. उदाहरण में, कमांड जारी होने पर स्क्रीन स्क्रीन थी।
ढोल की थाप

स्क्रीन और अलग-अलग फ़ाइलों में स्टडआउट और स्टॉडर दिखाने के लिए उदाहरण बहुत बढ़िया है !!! आपका बहुत बहुत धन्यवाद!
मार्सेल्लो डे सेल्स

21

किसी फ़ाइल में स्टैडर को पुनर्निर्देशित करने के लिए, स्क्रीन पर स्टडआउट प्रदर्शित करें, और स्टडआउट को एक फ़ाइल में सहेजें:

./aaa.sh 2> ccc.out | टी ।/bbb.out

संपादित करें : स्क्रीन पर stderr और stdout दोनों को प्रदर्शित करने के लिए और दोनों को फ़ाइल में सहेजने के लिए, आप bash I / O पुनर्निर्देशन का उपयोग कर सकते हैं :

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1

1
मुझे उम्मीद है कि उपयोगकर्ता फ़ाइल के अलावा उनके कंसोल पर जाना चाहता है, हालांकि ऐसा स्पष्ट रूप से निर्दिष्ट नहीं किया गया था।
चार्ल्स डफी

2
मुझे स्पष्ट होना चाहिए था, मैं स्क्रीन के लिए भी stderr चाहता था। मैंने फिर भी जोश केली के समाधान का आनंद लिया, लेकिन अपनी आवश्यकताओं के अनुरूप लुनानाथ को ढूंढा। धन्यवाद दोस्तों!
जपरानीच

18

दूसरे शब्दों में, आप stdout को एक फ़िल्टर ( tee bbb.out) और दूसरे फ़िल्टर में stderr ( ) में पाइप करना चाहते हैं tee ccc.out। किसी अन्य कमांड में stdout के अलावा किसी भी चीज़ को पाइप करने का कोई मानक तरीका नहीं है, लेकिन आप फ़ाइल डिस्क्रिप्टर के माध्यम से काम कर सकते हैं।

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

यह भी देखें कि मानक त्रुटि स्ट्रीम (stderr) grep कैसे करें? और आप एक अतिरिक्त फ़ाइल डिस्क्रिप्टर का उपयोग कब करेंगे?

बैश (और ksh और zsh) में, लेकिन डैश जैसे अन्य POSIX गोले में नहीं, आप प्रक्रिया विकल्प का उपयोग कर सकते हैं :

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

खबरदार कि बैश में, यह कमांड ./aaa.shखत्म होते ही वापस आ जाता है , भले ही teeकमांड्स अभी भी निष्पादित हो (ksh और zsh उपप्रकारों के लिए प्रतीक्षा करें)। यदि आप कुछ ऐसा करते हैं तो यह एक समस्या हो सकती है ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out। उस स्थिति में, इसके बजाय फ़ाइल डिस्क्रिप्टर बाजीगरी या ksh / zsh का उपयोग करें।


4
यह एकमात्र उत्तर की तरह लगता है जो stdout / stderr धाराओं को रखने की अनुमति देता है, जैसे (जैसे उन्हें विलय नहीं कर रहा है)। मिठाई!
user1338062 8

shक्रोन नौकरियों के लिए उपयोगी, जहां प्रक्रिया प्रतिस्थापन उपलब्ध नहीं है, सबसे सरल दृष्टिकोण ।
रोजर ड्यूक

13

यदि बैश का उपयोग कर रहे हैं:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

क्रेडिट (मेरे सिर के ऊपर से उत्तर नहीं) यहाँ जाता है: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html


5

मेरे मामले में, एक स्क्रिप्ट कमांड को चला रही थी, जो किसी फाइल में stdout और stderr दोनों को रीडायरेक्ट कर रही थी, कुछ इस तरह:

cmd > log 2>&1

मुझे इसे अपडेट करने की आवश्यकता थी जब विफलता होती है, तो त्रुटि संदेशों के आधार पर कुछ क्रियाएं करें। मैं निश्चित रूप से डंप को हटा सकता हूं 2>&1और स्क्रिप्ट से स्टेडर को कैप्चर कर सकता हूं , लेकिन फिर त्रुटि संदेश संदर्भ के लिए लॉग फ़ाइल में नहीं जाएगा। जबकि @ लुननाथ से स्वीकृत उत्तर ऐसा ही माना जाता है, यह रीडायरेक्ट करता है stdoutऔर stderrअलग-अलग फाइलों में होता है, जो कि मैं नहीं चाहता, लेकिन इसने मुझे उस सटीक समाधान के साथ आने में मदद की जिसकी मुझे आवश्यकता है:

(cmd 2> >(tee /dev/stderr)) > log

इसके बाद के संस्करण के साथ, लॉग दोनों की एक प्रति है जाएगा stdoutऔर stderrऔर मैं पर कब्जा कर सकते हैं stderrके बारे में चिंता किए बिना मेरी स्क्रिप्ट से stdout


4

निम्नलिखित KornShell (ksh) के लिए काम करेगा जहाँ प्रक्रिया प्रतिस्थापन उपलब्ध नहीं है,

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

यहां वास्तविक चाल, वह क्रम है 2>&1 1>&3जिसके बारे में हमारे मामले में पुनर्निर्देश करता stderrहै stdoutऔर stdoutवर्णनकर्ता को पुनर्निर्देशित करता है 3। इस बिंदु पर stderrऔर stdoutअभी तक संयुक्त नहीं हैं।

वास्तव में, stderr( जहां stdin) को पारित किया जाता है teeजहां यह लॉग इन करता है stderr.logऔर डिस्क्रिप्टर 3 पर भी रीडायरेक्ट करता है।

और डिस्क्रिप्टर 3इसे combined.logहर समय लॉग कर रहा है । तो combined.logदोनों शामिल हैं stdoutऔर stderr


निफ्टी! दया क्योंकि यह एक अच्छा विकल्प है। मेरे पास KSH, btw :)
runlevel0

2

यदि आप zsh का उपयोग कर रहे हैं , तो आप कई पुनर्निर्देशन का उपयोग कर सकते हैं, इसलिए आपको इसकी आवश्यकता नहीं है tee:

./cmd 1>&1 2>&2 1>out_file 2>err_file

यहाँ आप प्रत्येक स्ट्रीम को स्वयं और लक्ष्य फ़ाइल पर पुनर्निर्देशित कर रहे हैं ।


पूर्ण उदाहरण

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

ध्यान दें कि इसके लिए MULTIOSसेट करने का विकल्प आवश्यक है (जो कि डिफ़ॉल्ट है)।

MULTIOS

जब एकाधिक पुनर्निर्देशन का प्रयास किया जाता है ( पुनर्निर्देशन देखें ) teeएस अंतर्निहित एस या catएस करें ।

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