पाठक के लाभ के लिए, यह नुस्खा यहाँ
- एक चर में stderr को पकड़ने के लिए ऑनलाइनर के रूप में फिर से उपयोग किया जा सकता है
- अभी भी कमांड के रिटर्न कोड तक पहुंच देता है
- एक अस्थायी फ़ाइल डिस्क्रिप्टर 3 का बलिदान करता है (जो आपके द्वारा बदला जा सकता है)
- और इस अस्थायी फ़ाइल डिस्क्रिप्टर को आंतरिक कमांड में उजागर नहीं करता है
यदि आप stderrकुछ commandमें पकड़ना चाहते varहैं तो आप कर सकते हैं
{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
बाद में आप यह सब है:
echo "command gives $? and stderr '$var'";
अगर commandसरल है (कुछ ऐसा नहीं है a | b) तो आप भीतर को छोड़ {}सकते हैं:
{ var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;
एक आसान पुन: प्रयोज्य- bashसंयोजन में लिपटे (शायद संस्करण 3 और इसके लिए ऊपर की आवश्यकता है local -n):
: catch-stderr var cmd [args..]
catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }
व्याख्या की:
local -nउपनाम "$ 1" (जिसके लिए चर है catch-stderr)
3>&1 वहाँ stdout अंक को बचाने के लिए फ़ाइल डिस्क्रिप्टर 3 का उपयोग करता है
{ command; } (या "$ @") तब आउटपुट कैप्चरिंग के भीतर कमांड निष्पादित करता है $(..)
- कृपया ध्यान दें कि सटीक क्रम यहां महत्वपूर्ण है (इसे गलत तरीके से करने से फाइल डिस्क्रिप्टर गलत तरीके से बदल जाता है):
2>&1stderrआउटपुट कैप्चरिंग पर रीडायरेक्ट करता है$(..)
1>&3"बाहरी" stdoutपर $(..)वापस कैप्चर करने वाले आउटपुट से रीडायरेक्ट करता है stdoutजो फ़ाइल डिस्क्रिप्टर 3 में सेव किया गया था। ध्यान दें कि stderrअभी भी एफडी 1 से पहले बताया गया है: आउटपुट कैप्चरिंग के लिए$(..)
3>&-फिर फ़ाइल डिस्क्रिप्टर 3 को बंद कर देता है क्योंकि इसकी कोई आवश्यकता नहीं है, जैसे कि commandअचानक कोई अज्ञात फ़ाइल डिस्क्रिप्टर दिखाई नहीं देता है। ध्यान दें कि बाहरी शेल में अभी भी FD 3 खुली है, लेकिन commandइसे नहीं देखेंगे।
- उत्तरार्द्ध महत्वपूर्ण है, क्योंकि कुछ प्रोग्राम
lvmअप्रत्याशित फ़ाइल विवरणकर्ताओं के बारे में शिकायत करते हैं। और lvmशिकायत करता है stderr- बस जिसे हम पकड़ने जा रहे हैं!
आप इस नुस्खा के साथ किसी भी अन्य फ़ाइल डिस्क्रिप्टर को पकड़ सकते हैं, यदि आप तदनुसार अनुकूलित करते हैं। फ़ाइल डिस्क्रिप्टर 1 को छोड़कर (यहाँ पुनर्निर्देशन तर्क गलत होगा, लेकिन फ़ाइल डिस्क्रिप्टर 1 के लिए आप बस var=$(command)सामान्य रूप से उपयोग कर सकते हैं )।
ध्यान दें कि यह बलिदान फ़ाइल डिस्क्रिप्टर 3 है। यदि आपको उस फ़ाइल डिस्क्रिप्टर की आवश्यकता होती है, तो नंबर बदलने के लिए स्वतंत्र महसूस करें। लेकिन ध्यान रखें, कि कुछ गोले (1980 के दशक से) के बाद 99>&1तर्क के रूप में समझ सकते हैं (यह कोई समस्या नहीं है )।99>&1bash
यह भी ध्यान दें कि एक चर के माध्यम से इस FD 3 को विन्यास योग्य बनाना आसान नहीं है। यह चीजों को बहुत अपठनीय बनाता है:
: catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
catch-var-from-fd-by-fd()
{
local -n v="$1";
local fd1="$2" fd2="$3";
shift 3 || return;
eval exec "$fd2>&1";
v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
eval exec "$fd2>&-";
}
सुरक्षा नोट: पहले 3 तर्कों को catch-var-from-fd-by-fdतीसरे पक्ष से नहीं लिया जाना चाहिए। हमेशा उन्हें "स्थिर" फैशन में स्पष्ट रूप से दें।
तो नहीं-नहीं-नहीं catch-var-from-fd-by-fd $var $fda $fdb $command, ऐसा कभी मत करो!
यदि आप एक परिवर्तनशील चर नाम से गुजरते हैं, तो कम से कम इसे निम्नानुसार करें:
local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command
यह अभी भी हर शोषण के खिलाफ आपकी रक्षा नहीं करेगा, लेकिन कम से कम सामान्य स्क्रिप्टिंग त्रुटियों का पता लगाने और बचने में मदद करता है।
टिप्पणियाँ:
catch-var-from-fd-by-fd var 2 3 cmd.. के समान है catch-stderr var cmd..
shift || returnयदि आप सही तर्क देना भूल जाते हैं, तो बदसूरत त्रुटियों को रोकने के लिए बस कुछ तरीका है। शायद शेल को समाप्त करना एक और तरीका होगा (लेकिन इससे कमांडलाइन से परीक्षण करना कठिन हो जाता है)।
- दिनचर्या ऐसी लिखी गई थी, जिसे समझना ज्यादा आसान है। कोई फ़ंक्शन को फिर से लिख सकता है जैसे कि इसकी आवश्यकता नहीं है
exec, लेकिन फिर यह वास्तव में बदसूरत हो जाता है।
- इस दिनचर्या को गैर के लिए भी फिर से लिखा जा सकता है,
bashजैसे कि इसकी कोई आवश्यकता नहीं है local -n। हालाँकि तब आप स्थानीय चर का उपयोग नहीं कर सकते हैं और यह बहुत बदसूरत हो जाता है!
- यह भी ध्यान दें कि
evalएस एक सुरक्षित फैशन में उपयोग किया जाता है। आमतौर evalपर खतरनाक माना जाता है। हालांकि इस मामले में यह "$@"(मनमाना आदेशों को निष्पादित करने के लिए) उपयोग करने से ज्यादा बुराई नहीं है । हालाँकि कृपया यहाँ दिखाए गए अनुसार सटीक और सही उद्धरण का उपयोग करना सुनिश्चित करें (अन्यथा यह बहुत खतरनाक हो जाता है )।
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)