एक प्रोग्राम को कैसे पता चलता है कि स्टडआउट टर्मिनल या पाइप से जुड़ा है?


12

मैं एक segfaulting प्रोग्राम को डीबग करने में समस्या कर रहा हूं क्योंकि segfault से पहले ouput ठीक वही है जो मुझे चाहिए, लेकिन अगर मैं किसी फाइल में आउटपुट पाइप कर रहा हूं तो यह खो जाता है। इस उत्तर के अनुसार: /unix//a/17339/22615 , यह इसलिए है क्योंकि प्रोग्राम का आउटपुट बफ़र टर्मिनल से कनेक्ट होने पर तुरंत फ़्लश हो जाता है लेकिन पाइप से कनेक्ट होने पर केवल कुछ बिंदुओं पर। यहाँ कुछ प्रश्न:

  • एक कार्यक्रम कैसे निर्धारित करता है कि उसका स्टडआउट किससे जुड़ा है?

  • जब प्रोग्राम किसी टर्मिनल पर लिखता है तो "स्क्रिप्ट" कमांड उसी तरह का व्यवहार कैसे करता है?

  • क्या यह स्क्रिप्ट कमांड के बिना प्राप्त किया जा सकता है?


एक संबंधित प्रश्न unix.stackexchange.com/q/513926/5132 है
JdeBP

जवाबों:


23

यह बताने पर कि क्या कोई फाइल डिस्क्रिप्टर किसी टर्मिनल डिवाइस की ओर इशारा करता है

एक प्रोग्राम बता सकता है कि क्या कोई फ़ाइल डिस्क्रिप्टर isatty()मानक C फ़ंक्शन का उपयोग करके किसी ट्टी डिवाइस से जुड़ा है (जो आम तौर पर एक सहज-विशिष्ट tty- विशिष्ट ioctl()सिस्टम कॉल करता है जो त्रुटि के साथ वापस आ जाएगा जब fd एक tty डिवाइस को इंगित नहीं करता है ।

[/ testउपयोगिता इसके साथ यह कर सकते हैं -tऑपरेटर।

if [ -t 1 ]; then
  echo stdout is open to a terminal
fi

एक GNU / Linux सिस्टम पर ट्रेसिंग libc फ़ंक्शन कॉल:

$ ltrace [ -t 1 ] | cat
[...]
isatty(1)                                      = 0
[...]

ट्रेसिंग सिस्टम कॉल:

$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]

यह बताने पर कि क्या यह पाइप की ओर इशारा करता है

यह निर्धारित करने के लिए कि एक fd पाइप / फेनो से जुड़ा हुआ है, कोई भी fstat()सिस्टम कॉल का उपयोग कर सकता है , जो एक संरचना देता है जिसके st_modeक्षेत्र में उस fd पर खोली गई फ़ाइल का प्रकार और अनुमतियाँ होती हैं। S_ISFIFO()मानक सी मैक्रो उस पर इस्तेमाल किया जा सकता st_modeहै, तो एफडी एक पाइप है / फीफो निर्धारित करने के लिए क्षेत्र।

कोई मानक उपयोगिता नहीं है जो एक कर सकती है fstat(), लेकिन एक statकमांड के कई असंगत कार्यान्वयन हैं जो यह कर सकते हैं। zshवह statबिलियन stat -sf "$fd" +modeजिसके साथ मोड को एक स्ट्रिंग प्रतिनिधित्व के रूप में लौटाता है जिसका पहला चरित्र प्रकार ( pपाइप के लिए) का प्रतिनिधित्व करता है । GNU के साथ statभी ऐसा ही किया जा सकता है stat -c %A - <&"$fd", लेकिन अकेले प्रकारstat -c %F - <&"$fd" को भी रिपोर्ट करना होगा । बीएसडी के साथ : या ।statstat -f %St <&"$fd"stat -f %HT <&"$fd"

यह बताने योग्य है कि क्या है

आवेदन आम तौर पर परवाह नहीं है अगर stdout एक पाइप है, हालांकि। वे परवाह कर सकते हैं कि यह खोजी है (हालांकि आमतौर पर यह तय नहीं करना है कि बफर करना है या नहीं)।

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

zshऔर ksh93गोले हालांकि ऑपरेटरों की मांग निर्मित है:

$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
Illegal seek

बफरिंग को अक्षम करना

scriptआदेश एक कार्यक्रम के उत्पादन में कब्जा करने के लिए एक छद्म टर्मिनल जोड़ी का उपयोग करता है, तो कार्यक्रम के stdout (और stdin और stderr) एक छद्म टर्मिनल डिवाइस हो जाएगा।

जब स्टडआउट एक टर्मिनल डिवाइस पर होता है, तब भी आम तौर पर कुछ बफरिंग होती है, लेकिन यह लाइन आधारित होती है। printf/ putsऔर सह तब तक कुछ भी नहीं लिखेंगे जब तक कि कोई नया वर्ण आउटपुट नहीं होगा। अन्य प्रकार की फ़ाइलों के लिए, बफ़रिंग ब्लॉक (कुछ किलो बाइट्स) द्वारा होती है।

बफरिंग जो प्रश्नोत्तर यहाँ का एक संख्या में चर्चा कर रहे हैं निष्क्रिय करने के लिए कई विकल्प (के लिए खोज रहे हैं unbuffer या stdbuf , नहीं रीडायरेक्ट कटौती उत्पादन कर सकते हैं एक छद्म टर्मिनल के रूप में किया जा सकता है का उपयोग करके कुछ दृष्टिकोण देता है) या तो socat/ script/ expect/ unbuffer(एक expectस्क्रिप्ट) / zsh's zptyया GNU या FreeBSD's द्वारा किए गए बफरिंग को निष्क्रिय करने के लिए निष्पादन योग्य में कोड इंजेक्ट करके stdbuf


1
बहुत बढ़िया जवाब, इसके लिए बहुत-बहुत धन्यवाद!
मोवल्कलर

एक अन्य, लिनक्स-विशिष्ट दृष्टिकोण ट्रैवर्स /procडाइरेक्टरी के लिए है और प्रत्येक /proc/<integer>/डायरेक्टरी के /proc/<integer>/fd/लिए फाइल डिस्क्रिप्टर को देखता है और ढूंढता है जिसमें pipefs serverfault.com/q/48330/363611 में एक ही इनकोड नंबर है , लेकिन यह केवल स्क्रिप्ट में उपयोगी है जब कोई वर्णित syscalls का उपयोग नहीं कर सकता है। स्टीफन के जवाब में, और उचित समाधान की तुलना में एक समाधान के अधिक है IMHO
सर्जियो कोलोडियाज़नी

बीएसडी पर, lseekटर्मिनलों और अन्य चरित्र उपकरणों पर सफल होंगे, और बस एक काउंटर को फिर से सेट / सेट करेंगे जो प्रत्येक सफल रीड () पर बढ़ा है। मैं नहीं जानता कि क्या यह उन्हें "खोज योग्य" बनाता है।
19

@mowwwalker यदि इस उत्तर से आपकी समस्या हल हो गई है, तो कृपया एक क्षण लें और बाईं ओर चेक मार्क पर क्लिक करके इसे स्वीकार करें। यह प्रश्न के उत्तर के रूप में चिह्नित करेगा और स्टैक एक्सचेंज साइटों पर धन्यवाद व्यक्त करने का तरीका है।
मिठाई
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.