2011-08-16 12 views
49

क्या कोई मुझे बता सकता है कि यह क्यों काम नहीं करता है? मैं फाइल डिस्क्रिप्टर के साथ खेल रहा हूं, लेकिन थोड़ा हारा महसूस कर रहा हूं।फाइल डिस्क्रिप्टर कैसे काम करते हैं?

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

पहली तीन पंक्तियां ठीक चलती हैं, लेकिन अंतिम दो त्रुटिएं समाप्त होती हैं। क्यूं कर?

उत्तर

63

फाइल डिस्क्रिप्टर 0, 1 और 2 क्रमशः stdin, stdout और stderr के लिए हैं।

फ़ाइल वर्णनकर्ता 3, 4, .. 9 अतिरिक्त फ़ाइलों के लिए हैं। उनका उपयोग करने के लिए, आपको उन्हें पहले खोलना होगा। उदाहरण के लिए:

exec 3<> /tmp/foo #open fd 3. 
echo "test" >&3 
exec 3>&- #close fd 3. 

अधिक जानकारी के लिए Advanced Bash-Scripting Guide: Chapter 20. I/O Redirection पर एक नज़र डालें।

+1

यही वह है जिसे मैं ढूंढ रहा हूं! तो मुझे exec कमांड के साथ अस्थायी स्टोरेज स्थान के रूप में उपयोग करने के लिए एक फ़ाइल निर्दिष्ट करने की आवश्यकता है, और फिर जब मैं कर रहा हूं तो उन्हें बंद कर दें? क्षमा करें, मैं exec कमांड के साथ थोड़ा अस्पष्ट हूं, मैं इसका अधिक उपयोग नहीं करता हूं। – Trcx

+1

हाँ, लेकिन यह अस्थायी नहीं है। आपके प्रोग्राम को पूरा होने के बाद भी फ़ाइल मौजूद होगी। – dogbane

+0

यह अच्छी तरह से काम कर सकता है, तो मैं क्रॉन्टाब कार्य शेड्यूलर के साथ संगत होने के लिए कुछ स्क्रिप्ट को पोर्ट करने की कोशिश कर रहा हूं, लेकिन मुझे परेशानी हो रही है क्योंकि क्रॉन स्क्रिप्ट में stdout की पाइपिंग की अनुमति नहीं देता है। – Trcx

16

यह असफल रहा है क्योंकि उन फाइल डिस्क्रिप्टर कुछ भी इंगित नहीं करते हैं! सामान्य डिफ़ॉल्ट फ़ाइल वर्णनकर्ता मानक इनपुट 0, मानक आउटपुट 1, और मानक त्रुटि स्ट्रीम 2 हैं। चूंकि आपकी स्क्रिप्ट किसी अन्य फाइल को नहीं खोल रही है, इसलिए कोई अन्य वैध फ़ाइल डिस्क्रिप्टर नहीं हैं। आप exec का उपयोग कर बैश में एक फ़ाइल खोल सकते हैं। यहाँ अपने उदाहरण के एक संशोधन है:

#!/bin/bash 
exec 3> out1  # open file 'out1' for writing, assign to fd 3 
exec 4> out2  # open file 'out2' for writing, assign to fd 4 

echo "This"  # output to fd 1 (stdout) 
echo "is" >&2 # output to fd 2 (stderr) 
echo "a" >&3  # output to fd 3 
echo "test." >&4 # output to fd 4 

और अब हम इसे चलाने होगी:

$ ls 
script 
$ ./script 
This 
is 
$ ls 
out1 out2 script 
$ cat out* 
a 
test. 
$ 

आप देख सकते हैं, अतिरिक्त उत्पादन का अनुरोध किया फ़ाइलों को भेजा गया था।

+0

क्या कोई तरीका है कि मैं इसे टर्मिनल में डाल सकता हूं? मैं टर्मिनल में इसे देखने में सक्षम होना चाहता हूं, लेकिन जहां मैं चाहता हूं आउटपुट भेजने में सक्षम होना चाहता हूं। i.e./script 2> out.2 3> out.3 4> out.4 – Trcx

+0

@Trcx, यदि आप टर्मिनल पर लिखना चाहते हैं, तो 'stdout' या' stderr' का उपयोग करें। इसके लिए आपको अन्य फाइलों का उपयोग क्यों करना होगा या नहीं? –

+0

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

35

यह एक पुराना सवाल है लेकिन एक चीज़ को स्पष्टीकरण की आवश्यकता है।

जबकि कार्ल नोरम और कुत्ते के जवाब सही हैं, धारणा काम करने के लिए अपनी स्क्रिप्ट को बदलती है।

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

यह काम करता है अगर आप इसे दूसरे तरीके से आह्वान:

./fdtest 3>&1 4>&1 

जिसका अर्थ है

मैं बाहर बिंदु करना चाहते हैं क्या है कि आप स्क्रिप्ट को बदलने की जरूरत नहीं है फाइल डिस्क्रिप्टर 3 और 4 से 1 को रीडायरेक्ट करने के लिए (जो मानक आउटपुट है)।

मुद्दा यह है कि स्क्रिप्ट पूरी तरह से सिर्फ 1 और 2 (stdout और stderr) के अलावा अन्य वर्णनकर्ता में लिखने के लिए उन वर्णनकर्ता माता पिता प्रक्रिया द्वारा प्रदान की जाती है, तो चाहने में ठीक है।

आपका उदाहरण क्योंकि इस स्क्रिप्ट 4 अलग फ़ाइलों को लिख सकते हैं वास्तव में काफी दिलचस्प है:

./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

अब आप 4 अलग फ़ाइलों में उत्पादन:

$ for f in file*; do echo $f:; cat $f; done 
file1.txt: 
This 
file2.txt: 
is 
file3.txt: 
a 
file4.txt: 
test. 

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

उदाहरण के लिए

, जब मैं sudo -s चलाने के लिए, रूट करने के लिए उपयोगकर्ता को बदलने रूट के रूप में एक निर्देशिका बनाने, और मेरे नियमित उपयोगकर्ता इस तरह (मेरे मामले में आरएसपी) के रूप में निम्न आदेश को चलाने के लिए प्रयास करने के लिए:

# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt' 

मैं एक त्रुटि मिलती है:

bash: file1.txt: Permission denied 

लेकिन अगर मैं su के बाहर पुनर्निर्देशन कार्य करें:

# su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

(एकल उद्धरण में अंतर पर ध्यान दें) यह काम करता है और मैं:

# ls -alp 
total 56 
drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./ 
drwxrwxr-x 3 rsp rsp 4096 Jun 23 15:01 ../ 
-rw-r--r-- 1 root root 5 Jun 23 15:05 file1.txt 
-rw-r--r-- 1 root root 39 Jun 23 15:05 file2.txt 
-rw-r--r-- 1 root root 2 Jun 23 15:05 file3.txt 
-rw-r--r-- 1 root root 6 Jun 23 15:05 file4.txt 

जो 4 जड़ के स्वामित्व वाले एक निर्देशिका में जड़ के स्वामित्व वाली फ़ाइलों कर रहे हैं - भले ही स्क्रिप्ट नहीं था उन फ़ाइलों को बनाने के लिए अनुमति

एक और उदाहरण क्रोट जेल या कंटेनर का उपयोग करेगा और एक प्रोग्राम चलाएगा जहां उसे रूट के रूप में चलाने के बावजूद उन फ़ाइलों तक पहुंच नहीं होगी और फिर भी उन डिस्क्रिप्टर को बाहरी रूप से रीडायरेक्ट करें जहां आपको आवश्यकता है पूरी फाइल सिस्टम या इस स्क्रिप्ट के लिए और कुछ भी।

बिंदु यह है कि आपने एक बहुत ही रोचक और उपयोगी तंत्र खोजा है। आपको अन्य उत्तरों में सुझाए गए अनुसार आपकी स्क्रिप्ट के अंदर सभी फाइलें खोलने की ज़रूरत नहीं है। कभी-कभी स्क्रिप्ट आमंत्रण के दौरान उन्हें पुनर्निर्देशित करना उपयोगी होता है।

यह कुल मिलाकर के लिए, इस:

echo "This" >&1 

और कार्यक्रम चलाने के रूप में:

./program >file.txt 

रूप में ही है

echo "This" 

वास्तव में के बराबर है:

./program 1>file.txt 

नंबर 1 केवल एक डिफ़ॉल्ट संख्या है और यह stdout है।

लेकिन फिर भी इस कार्यक्रम:

#!/bin/bash 
echo "This" 

एक "बुरा वर्णनकर्ता" त्रुटि उत्पादन कर सकते हैं। कैसे? जब के रूप में चलाने:

./fdtest2 >&- 

उत्पादन होगा:

./fdtest2: line 2: echo: write error: Bad file descriptor 

>&- जोड़ा जा रहा है (जो 1>&- के समान है) मानक आउटपुट को बंद करने का मतलब है। 2>&- जोड़ना मतलब है stderr बंद करना।

आप भी एक और जटिल चीज कर सकते हैं।आपका मूल स्क्रिप्ट:

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

बस के साथ चलते हैं तो:

./fdtest 

प्रिंट:

This 
is 
./fdtest: line 4: 3: Bad file descriptor 
./fdtest: line 5: 4: Bad file descriptor 

लेकिन आप वर्णनकर्ता 3 और 4 काम कर सकते हैं, लेकिन नंबर 1 चलाकर असफल:

./fdtest 3>&1 4>&1 1>&- 

यह आउटपुट:

./fdtest: line 2: echo: write error: Bad file descriptor 
is 
a 
test. 

आप वर्णनकर्ता दोनों 1 और 2 असफल हो, यह इस तरह से चलाने चाहते हैं:

./fdtest 3>&1 4>&1 1>&- 2>&- 

आप मिल:

a 
test. 

क्यों? कुछ भी असफल नहीं हुआ? यह था लेकिन बिना stderr (फ़ाइल वर्णनकर्ता संख्या 2) आपको त्रुटि संदेश नहीं दिखाई दिए!

मुझे लगता है कि वर्णनकर्ताओं और उनके पुनर्निर्देशन के काम को महसूस करने के लिए इस तरह प्रयोग करना बहुत उपयोगी है।

आपकी स्क्रिप्ट वास्तव में एक बहुत ही रोचक उदाहरण है - और मैं तर्क देता हूं कि यह बिल्कुल टूटा नहीं है, आप बस इसे गलत इस्तेमाल कर रहे थे! :)

+0

वी दिलचस्प प्रतिक्रिया .. धन्यवाद –

+0

असल में, फाइल डिस्क्रिप्टर 1 stdout है; stdin फ़ाइल descriptor 0. – programmerjake

+0

@programmerjake ओह है। टाइपो तय इस पर ध्यान दिलाने के लिए धन्यवाद। – rsp

संबंधित मुद्दे