मैं stderr को कैसे पाइप कर सकता हूं, और stdout को नहीं?


981

मैं एक प्रोग्राम है जो करने के लिए जानकारी लिखता है stdoutऔर stderr, और मैं करने की आवश्यकता है grepके माध्यम से क्या करने के लिए आ रहा है stderr , जबकि अनदेखी stdout

मैं इसे 2 चरणों में कर सकता हूं:

command > /dev/null 2> temp.file
grep 'something' temp.file

लेकिन मैं अस्थायी फ़ाइलों के बिना ऐसा करने में सक्षम होना पसंद करूंगा। क्या कोई स्मार्ट पाइपिंग ट्रिक्स हैं?


इसी तरह का एक सवाल, लेकिन स्टैडआउट को
joeytwiddle

यह प्रश्न बैश के लिए था, लेकिन बॉर्न / अल्मक्विस्ट शेल के लिए इस संबंधित लेख का उल्लेख करने योग्य है।
स्टीफन Niedzielski

10
मैं कुछ इस तरह की उम्मीद कर रहा था command 2| othercommand:। बैश इतना सही है कि विकास 1982 में समाप्त हो गया था, इसलिए हम कभी भी यह नहीं देखेंगे कि मैं डर रहा हूं।
रॉल्फ

जवाबों:


1188

स्टडआउट के लिए पहले पुनर्निर्देशित स्टेडर - पाइप; इसके बाद stdout को रीडायरेक्ट करें /dev/null(बिना बदले जहां स्टैडर जा रहा है):

command 2>&1 >/dev/null | grep 'something'

सभी प्रकार में I / O पुनर्निर्देशन के विवरण के लिए, बैश संदर्भ पुस्तिका में पुनर्निर्देशन पर अध्याय देखें ।

ध्यान दें कि I / O पुनर्निर्देशन के अनुक्रम को बाएं से दाएं व्याख्या किया गया है, लेकिन I / O पुनर्निर्देशन की व्याख्या करने से पहले पाइप स्थापित किए जाते हैं। फ़ाइल विवरणक जैसे कि 1 और 2, फ़ाइल विवरण खोलने के संदर्भ हैं। ऑपरेशन 2>&1फ़ाइल डिस्क्रिप्टर 2 उर्फ ​​stderr को एक ही ओपन फाइल डिस्क्रिप्शन के रूप में संदर्भित करता है क्योंकि फाइल डिस्क्रिप्टर 1 उर्फ ​​स्टडआउट वर्तमान में (देखें dup2()और open()) का जिक्र कर रहा है । >/dev/nullतब ऑपरेशन फ़ाइल डिस्क्रिप्टर 1 को बदल देता है, ताकि यह उसके लिए एक ओपन फाइल विवरण को संदर्भित करता है /dev/null, लेकिन यह इस तथ्य को नहीं बदलता है कि फाइल डिस्क्रिप्टर 2 ओपन फाइल विवरण को संदर्भित करता है, जो फाइल डिस्क्रिप्टर 1 मूल रूप से - अर्थात्, पाइप को इंगित करता था।


44
मैं बस / देव / stdout / dev / stderr / dev / stdin के दूसरे दिन भर में ठोकर खाई, और मैं उत्सुक था कि क्या वे एक ही काम करने के अच्छे तरीके हैं? मुझे हमेशा लगता था कि 2> और 1 थोड़ा मोटे थे। तो कुछ इस तरह है: command 2> /dev/stdout 1> /dev/null | grep 'something'
माइक लून

17
आप /dev/stdoutएट अल का उपयोग कर सकते हैं , या उपयोग कर सकते हैं /dev/fd/N। जब तक शेल उन्हें विशेष मामलों के रूप में मानता है, तब तक वे मामूली रूप से कम कुशल होंगे; शुद्ध सांख्यिक अंकन में नाम से फ़ाइलों तक पहुँच शामिल नहीं है, लेकिन उपकरणों का उपयोग करने का मतलब फ़ाइल नाम की खोज है। क्या आप माप सकते हैं कि यह बहस का विषय है। मुझे संख्यात्मक अंकन की संक्षिप्तता पसंद है - लेकिन मैं इसे इतने लंबे समय से उपयोग कर रहा हूं (एक चौथाई शताब्दी से अधिक; ouch!) कि मैं आधुनिक दुनिया में इसकी खूबियों को आंकने के योग्य नहीं हूं।
जोनाथन लेफ्लर

23
@ जोनाथन लेफ़लर: मैं आपके सादे पाठ की व्याख्या के साथ थोड़ा मुद्दा लेता हूं der रीडायरेक्ट स्टडर टू स्टडआउट और फिर स्टडआउट टू / देव / नल ’ - चूंकि किसी को दाएं से बाएं (बाएं से दाएं नहीं) रीडायरेक्शन चेन पढ़ना है, हम इसके लिए हमारी सादा पाठ व्याख्या को भी अनुकूलित करना चाहिए: 'रीडायरेक्ट stdout to / dev / null, और फिर stderr जहाँ stdout हुआ करता था'
कर्ट फ़ेफ़ेले जूल

116
@ कर्टपाइफ़ल: औ गर्भनिरोधक! किसी को बाएं से दाएं पुनर्निर्देशन श्रृंखलाओं को पढ़ना चाहिए क्योंकि शेल उन्हें संसाधित करता है। पहला ऑपरेशन है 2>&1, जिसका अर्थ है कि '' फ़ाइल डिस्क्रिप्टर से stderr कनेक्ट करें जो stdout वर्तमान में जा रहा है '। दूसरा ऑपरेशन 'चेंज स्टैडआउट है तो यह हो जाता है /dev/null', स्टादर को मूल स्टडआउट, पाइप पर जा रहा है। शेल पहले पाइप प्रतीक पर चीजों को विभाजित करता है, इसलिए, पाइप पुनर्निर्देशन पहले 2>&1या >/dev/nullपुनर्निर्देशन से पहले होता है , लेकिन यह सब है; अन्य ऑपरेशन बाएं से दाएं हैं। (राइट-टू-लेफ्ट काम नहीं करेगा।)
जोनाथन लेफ़लर

14
जो चीज मुझे वास्तव में आश्चर्यचकित करती है, वह यह है कि यह विंडोज पर काम करती है, भी ( /dev/nullविंडोज समकक्ष के नाम बदलने के बाद nul)।
माइकल बूर

364

या मानक त्रुटि और मानक उत्पादन से अधिक उत्पादन को स्वैप करने के लिए, उपयोग करें:

command 3>&1 1>&2 2>&3

यह एक नई फ़ाइल डिस्क्रिप्टर (3) बनाता है और इसे 1 (मानक आउटपुट) के समान स्थान पर असाइन करता है, फिर fd 1 (मानक आउटपुट) को उसी स्थान पर fd 2 (मानक त्रुटि) के रूप में असाइन करता है और अंत में fd 2 (मानक त्रुटि) असाइन करता है ) fd 3 (मानक आउटपुट) के समान स्थान पर।

मानक त्रुटि अब मानक आउटपुट के रूप में उपलब्ध है और पुराना मानक आउटपुट मानक त्रुटि में संरक्षित है। यह ओवरकिल हो सकता है, लेकिन यह बश फ़ाइल विवरणकों पर अधिक विवरण देता है (प्रत्येक प्रक्रिया के लिए नौ उपलब्ध हैं)।


100
एक अंतिम ट्विस्ट 3>&-उस अतिरिक्त डिस्क्रिप्टर को बंद करने के लिए होगा जिसे आपने stdout से बनाया है
जोनाथन लेफ़लर

1
क्या हम एक फाइल डिस्क्रिप्टर बना सकते हैं जिसमें एक stderrऔर है जिसका संयोजन है stderrऔर stdout? दूसरे शब्दों stderrमें एक साथ दो अलग-अलग फाइलों में जा सकते हैं?
स्टुअर्ट

निम्न अभी भी स्टडआउट करने के लिए त्रुटियों को प्रिंट करता है। मैं क्या खो रहा हूँ? ls -l not_a_file 3> & 1 1> & 2 2> & 3> त्रुटियाँ। txt
user48956

1
@ JonasDahlbæk: ट्विक मुख्य रूप से चिड़चिड़ापन का एक मुद्दा है। वास्तव में आर्कैन स्थितियों में, यह ईओएफ का पता लगाने और पता लगाने की प्रक्रिया के बीच अंतर कर सकता है, लेकिन इसके लिए बहुत अजीब परिस्थितियों की आवश्यकता होती है।
जोनाथन लेफलर

1
सावधानी : यह मानता है कि एफडी 3 पहले से उपयोग में नहीं है, इसे बंद नहीं करता है, और फ़ाइल डिस्क्रिप्टर 1 और 2 की अदला-बदली नहीं करता है, इसलिए आप इसे अभी तक किसी अन्य कमांड को पाइप करने के लिए नहीं जा सकते। आगे विस्तार और काम के लिए इस उत्तर को देखें । {Ba, z} श के लिए बहुत अधिक क्लीनर सिंटैक्स के लिए, यह उत्तर देखें ।
टॉम हेल

218

बैश में, आप प्रक्रिया प्रतिस्थापन के उपयोग से उप-पुनर्निर्देशन भी कर सकते हैं :

command > >(stdlog pipe)  2> >(stderr pipe)

हाथ में मामले के लिए:

command 2> >(grep 'something') >/dev/null

1
स्क्रीन पर आउटपुट के लिए बहुत अच्छी तरह से काम करता है। क्या आपके पास कोई विचार है कि अगर मैं grep आउटपुट को किसी फ़ाइल में रीडायरेक्ट करता हूं तो अनरग्ड कंटेंट फिर से क्यों दिखाई देता है? command 2> >(grep 'something' > grep.log)Grep.log के बाद ungrepped.log से समान उत्पादन होता हैcommand 2> ungrepped.log
टिम

9
का उपयोग करें 2> >(stderr pipe >&2)। अन्यथा "stderr पाइप" का आउटपुट "stdlog पाइप" से होकर गुजरेगा।
छत

हाँ !, 2> >(...)काम करता है, मैंने कोशिश की, 2>&1 > >(...)लेकिन यह नहीं था
डेटिनडोकॉक

यहाँ एक छोटा सा उदाहरण है जो अगली बार मेरी मदद कर सकता है कि मैं यह कैसे करूँ। निम्नलिखित पर विचार करें ... awk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 ) इस उदाहरण में मैं यह भी देखना चाहता था कि मेरे कंसोल पर त्रुटियां क्या थीं। लेकिन STDOUT आउटपुट फाइल में जा रहा था। इसलिए उप-शेल के अंदर, आपको उस पुनर्निर्देशन की आवश्यकता है जो कोष्ठक के अंदर STDERR पर वापस जाती है। जबकि वह काम करता है, फ़ाइल teeके अंत में कमांड विंड-अप से STDOUT आउटपुट out-content.txt। वह मुझे असंगत लगता है।
होगा

@ दत्तदीनचौक मैंने इसे किसी तरह किया था2>&1 1> >(dest pipe)
अलीरज़ा मोहम्मदी

195

यदि आप करते हैं, तो इन उत्तरों में से सबसे अच्छा संयोजन:

command 2> >(grep -v something 1>&2)

... तो सभी stdout को stdout के रूप में संरक्षित किया जाता है और सभी stderr को stderr के रूप में संरक्षित किया जाता है, लेकिन आपको stderr में कोई रेखा नहीं दिखाई देगी जिसमें "कुछ" लिखा हो।

इसका सीधा फायदा स्टडआउट और स्टडर को न तो पलटने या छोड़ने में होता है, न ही इन्हें एक साथ स्मूच करने और न ही किसी अस्थायी फाइल के इस्तेमाल से।


नहीं है command 2> >(grep -v something)(बिना 1>&2) एक ही?
फ्रांसेक रोजास

11
नहीं, इसके बिना, फ़िल्टर किए गए स्टैडर को स्टडआउट करने के लिए रूट किया जाता है।
Pinko

1
यह वही है जो मुझे चाहिए - टार आउटपुट "फ़ाइल बदल गई क्योंकि हमने इसे एक निर्देशिका के लिए हमेशा पढ़ा", इसलिए बस उस एक लाइन को फ़िल्टर करना चाहते हैं लेकिन देखें कि क्या कोई अन्य त्रुटि होती है। इसलिए tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)काम करना चाहिए।
razzed

"स्ट्रिंग के साथ शुरुआत" कहना गलत है। Grep के प्रस्तुत सिंटैक्स के बारे में कुछ भी नहीं है जो इसे केवल उन लाइनों को बाहर कर देगा जो दी गई स्ट्रिंग से शुरू होती हैं। यदि वे दिए गए स्ट्रिंग में कहीं भी हैं, तो लाइनों को बाहर रखा जाएगा।
माइक नाकिस

@ मायनिकिस धन्यवाद - तय! (यह मेरे मूल उत्तर ड्राफ्ट से बचा हुआ था, जिसमें यह समझ में आया ...)
पिंको

102

यदि आप वास्तव में "रीडायरेक्ट" और "पाइप" के साथ क्या हो रहा है, इसके बारे में सोचते हैं तो चीजों को कल्पना करना बहुत आसान है। बैश में रीडायरेक्ट और पाइप एक काम करते हैं: जहां प्रक्रिया फ़ाइल विवरणकों को 0, 1, और 2 को संशोधित करती है (देखें / proc / [pid] / fd / *)।

जब एक पाइप या "|" ऑपरेटर कमांड लाइन पर मौजूद होता है, पहली बात यह है कि बैश एक फेनो बनाता है और लेफ्ट साइड कमांड की एफडी 1 को इस फि ल्म में इंगित करता है, और राइट साइड कमांड की एफडी 0 को उसी फि ल्म को इंगित करता है।

इसके बाद, प्रत्येक पक्ष के लिए पुनर्निर्देशित ऑपरेटरों का मूल्यांकन बाईं से दाईं ओर किया जाता है , और वर्तमान सेटिंग्स का उपयोग तब किया जाता है जब भी विवरणकर्ता का दोहराव होता है। यह महत्वपूर्ण है क्योंकि चूंकि पाइप को पहले स्थापित किया गया था, इसलिए FD1 (बाईं ओर) और FD0 (दाईं ओर) पहले से ही बदल दिए गए हैं जो कि वे सामान्य रूप से हो सकते हैं, और इनमें से कोई भी दोहराव उस तथ्य को प्रतिबिंबित करेगा।

इसलिए, जब आप निम्नलिखित जैसा कुछ लिखते हैं:

command 2>&1 >/dev/null | grep 'something'

यहाँ क्या होता है, क्रम में:

  1. एक पाइप (फीफो) बनाया जाता है। "कमांड FD1" इस पाइप को इंगित किया गया है। "grep FD0" भी इस पाइप को इंगित करता है
  2. "कमांड FD2" को इंगित किया जाता है, जहां "कमांड FD1" वर्तमान में इंगित करता है (पाइप)
  3. "कमांड FD1" को / dev / null को इंगित किया गया है

इसलिए, सभी आउटपुट जो "कमांड" अपने FD 2 (stderr) को लिखते हैं, पाइप के लिए अपना रास्ता बनाता है और दूसरी तरफ "grep" द्वारा पढ़ा जाता है। सभी आउटपुट जो "कमांड" अपने FD 1 (stdout) को लिखते हैं, वह / dev / null के लिए अपना रास्ता बनाता है।

यदि इसके बजाय, आप निम्न कार्य करते हैं:

command >/dev/null 2>&1 | grep 'something'

यहाँ क्या होता है:

  1. एक पाइप बनाया गया है और "कमांड एफडी 1" और "जीआरपी एफडी 0" को इंगित किया गया है
  2. "कमांड FD 1" को / dev / null को इंगित किया गया है
  3. "कमांड एफडी 2" को इंगित किया जाता है, जहां एफडी 1 वर्तमान में इंगित करता है (/ देव / शून्य)

तो, सभी कमांडआउट और "कमांड" से stderr / dev / null पर जाते हैं। पाइप में कुछ भी नहीं जाता है, और इस तरह "grep" स्क्रीन पर कुछ भी प्रदर्शित किए बिना बंद हो जाएगा।

यह भी ध्यान दें कि रीडायरेक्ट (फ़ाइल डिस्क्रिप्टर) केवल-पढ़ने के लिए (<), राइट-ओनली (>), या रीड-राइट (<>) हो सकते हैं।

एक अंतिम नोट। चाहे कोई प्रोग्राम FD1 या FD2 में कुछ लिखता है, पूरी तरह से प्रोग्रामर पर निर्भर है। अच्छा प्रोग्रामिंग अभ्यास यह निर्धारित करता है कि त्रुटि संदेश एफडी 2 और सामान्य आउटपुट एफडी 1 पर जाना चाहिए, लेकिन आप अक्सर मैला प्रोग्रामिंग पाएंगे जो दोनों को मिलाते हैं या अन्यथा सम्मेलन की उपेक्षा करते हैं।


6
वास्तव में अच्छा जवाब। मेरा एक सुझाव "फीफो" के अपने पहले उपयोग को "फीफो (एक नामित पाइप)" से बदलना होगा। मैं थोड़ी देर के लिए लिनक्स का उपयोग कर रहा हूं, लेकिन किसी भी तरह कभी भी यह जानने में कामयाब नहीं हुआ कि नामित पाइप के लिए एक और शब्द है। इसने मुझे इसे देखने से बचाया होगा, लेकिन फिर मैंने उस दूसरे सामान को नहीं सीखा होगा जो मैंने देखा था जब मुझे पता चला था!
मार्क एडिंगटन

3
@MarkEdington कृपया ध्यान दें कि FIFO पाइप और IPC के संदर्भ में नामित पाइप के लिए केवल एक और शब्द है । अधिक सामान्य संदर्भ में, एफआईएफओ का अर्थ है फर्स्ट इन, फर्स्ट आउट, जो एक कतार डेटा संरचना से सम्मिलन और हटाने का वर्णन करता है।
लोमचाइल्ड

5
@ निश्चित रूप से देखें। मेरी टिप्पणी का मुद्दा यह था कि एक अनुभवी डेवलपर के रूप में, मैंने कभी भी FIFO को पाइप नाम के पर्याय के रूप में इस्तेमाल नहीं किया था। दूसरे शब्दों में, मुझे यह नहीं पता था: en.wikipedia.org/wiki/FIFO_(computing_and_electronics)#Pipes - जवाब में स्पष्ट किया कि मुझे समय की बचत होगी।
मार्क एडिंगटन

39

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

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines


4
नहींं, |&बराबर है 2>&1जो स्टडआउट और स्टेडर को जोड़ती है। स्पष्ट रूप से स्टैडआउट के बिना आउटपुट के लिए पूछा गया प्रश्न ।
प्रोफत्सच

3
„यदि '| &' का उपयोग किया जाता है, कमांड 1 की मानक त्रुटि पाइप के माध्यम से कमांड 2 के मानक इनपुट से जुड़ी है; यह 2> और 1 के लिए आशुलिपि है। ” अपने लिंक पर चौथे पैराग्राफ से शब्दशः लिया।
प्रोफ़ेसटच

9
@Profpatsch: केन का उत्तर सही है, देखो कि वह stdout और stderr को मिलाने से पहले stdout को रीडायरेक्ट करता है, इसलिए आपको पाइप में केवल stderr मिलेगा, क्योंकि stdout को पहले / dev / null पर ड्रॉप किया गया था।
लुसियानो

3
लेकिन मुझे अभी भी लगा कि आपका उत्तर गलत है, >/dev/null |&विस्तार करें >/dev/null 2>&1 | और इसका मतलब है कि stdout इनोड पाइप से खाली है क्योंकि कोई भी (# 1 # 2 दोनों / / dev / null inode से बंधा हुआ है) stdout इनोड से बंधा हुआ है (जैसे ls -R /tmp/* >/dev/null 2>&1 | grep iखाली देगा, लेकिन ls -R /tmp/* 2>&1 >/dev/null | grep i# देगा 2 जो स्टडआउट इनोड से बंधेगी)।
फ्रूट

3
केन तीव्र, मैं परीक्षण किया है, और ( echo out; echo err >&2 ) >/dev/null |& grep "."कोई निर्गम (जहां हम "अरे" चाहते हैं) देता है। man bashकहते हैं यदि | & का उपयोग किया जाता है ... 2 के लिए आशुलिपि है> और 1 | मानक आउटपुट के लिए मानक त्रुटि का यह अंतर्निहित पुनर्निर्देशन कमांड द्वारा निर्दिष्ट किसी भी पुनर्निर्देशन के बाद किया जाता है। तो पहले हम कमांड के FD1 को null में रीडायरेक्ट करते हैं, फिर हम कमांड के FD2 को FD1 की ओर निर्देशित करते हैं, अर्थात। अशक्त, इसलिए grep के FD0 को कोई इनपुट नहीं मिलता है। अधिक गहराई से स्पष्टीकरण के लिए stackoverflow.com/a/18342079/69663 देखें ।
अनहमर

11

उन लोगों के लिए जो स्थायी रूप से फ़ाइलों के लिए stdout और stderr को पुनर्निर्देशित करना चाहते हैं, stderr पर grep, लेकिन एक tty को संदेश लिखने के लिए stdout को रखें:

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

6

यह कमांड 1 स्टैडर को कमांड 2 स्टड पर पुनर्निर्देशित करेगा, जबकि कमांड 1 स्टडआउट जैसा है।

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

एलडीपी से लिया गया


2

मैं सिर्फ stdoutएक कमांड और stderrदूसरे को भेजने के लिए एक समाधान के साथ आया था , नामित पाइप का उपयोग करके।

यहाँ जाता हैं।

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

बाद में नामित पाइप को हटाने के लिए यह एक अच्छा विचार है।


0

आप आरसी शेल का उपयोग कर सकते हैं ।

पहले पैकेज स्थापित करें (यह 1 एमबी से कम है)।

इस तरह आप मानक आउटपुट और करने के लिए पाइप मानक त्रुटि त्यागने का एक उदाहरण ग्रेप में rc:

find /proc/ >[1] /dev/null |[2] grep task

आप इसे बैश छोड़ने के बिना कर सकते हैं:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

जैसा कि आपने देखा होगा, आप निर्दिष्ट कर सकते हैं कि आप किस फाइल डिस्क्रिप्टर को पाइप के बाद ब्रैकेट का उपयोग करके पाइप करना चाहते हैं।

मानक फ़ाइल विवरणकों को इस तरह से संख्यात्मक किया जाता है:

  • 0: मानक इनपुट
  • 1: मानक उत्पादन
  • 2: मानक त्रुटि

-3

मैं पालन करने की कोशिश करता हूं, इसे भी काम करता हूं,

command > /dev/null 2>&1 | grep 'something'

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