bash कैसे पाइप stderr करने के लिए, और stdout नहीं?




grep pipe (8)

मेरे पास एक ऐसा कार्यक्रम है जो stdout और stderr को जानकारी लिखता है, और मुझे stdout को अनदेखा करते समय, stderr पर आने वाले चीज़ों के माध्यम से grep करने की जरूरत है।

मैं निश्चित रूप से इसे 2 चरणों में कर सकता हूं:

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

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


मैं अनुवर्ती कोशिश करता हूं, इसे भी ढूंढता हूं,

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

यह कमांड 1 stdout को कमांड 2 stdin पर रीडायरेक्ट करेगा, जबकि कमांड 1 stdout को छोड़कर।

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

LDP से लिया गया


बैश में, आप प्रक्रिया प्रतिस्थापन का उपयोग करके एक सबहेल पर भी रीडायरेक्ट कर सकते हैं:

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

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

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

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

# 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

या उपयोग पर stderr और stdout से आउटपुट स्वैप करने के लिए: -

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

यह एक नई फ़ाइल डिस्क्रिप्टर (3) बनाता है और इसे 1 (stdout) के समान स्थान पर असाइन करता है, फिर fd 2 (stdr) के समान स्थान पर fd 1 (stdout) असाइन करता है और अंत में fd 2 (stderr) को उसी पर असाइन करता है एफडी 3 (stdout) के रूप में रखें। Stderr अब stderr में संरक्षित stdout और पुराने stdout के रूप में उपलब्ध है। यह अधिक हो सकता है लेकिन उम्मीद है कि बैश फ़ाइल डिस्क्रिप्टर पर अधिक जानकारी देता है (प्रत्येक प्रक्रिया में 9 उपलब्ध हैं)।


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

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

इसका स्टॉउट और स्टडरर को उलट या हटाने का अनूठा लाभ नहीं है, न ही उन्हें एक साथ जोड़ना, न ही किसी भी अस्थायी फ़ाइलों का उपयोग करना।





stderr