यदि मेरी शेल स्क्रिप्ट एक पाइप के माध्यम से चल रही है तो कैसे पता करें?


252

यदि शेल मानक के भीतर टर्मिनल पर भेजा जा रहा है या यदि इसे किसी अन्य प्रक्रिया में डाला जाता है, तो मुझे शेल स्क्रिप्ट से कैसे पता चलेगा?

बिंदु में मामला: मैं आउटपुट को रंगीन करने के लिए एस्केप कोड जोड़ना चाहूंगा, लेकिन केवल जब इंटरेक्टिव रूप से चलाया जाता है, लेकिन जब पाइप नहीं किया जाता है, तो क्या ls --colorहोता है।


2
यहाँ कुछ और दिलचस्प परीक्षण मामले हैं! < a href=" serverfault.com/questions/156470/… एक स्क्रिप्ट के लिए जो stdin<

2
@ user940324 सही लिंक serverfault.com/q/156470/197218 है
Palec

जवाबों:


385

एक शुद्ध POSIX शेल में,

if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi

"टर्मिनल" लौटाता है, क्योंकि आउटपुट आपके टर्मिनल पर भेजा जाता है, जबकि

(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat

"टर्मिनल नहीं" लौटाता है, क्योंकि पैतृक के उत्पादन को पाइप किया जाता है cat


-tध्वज के रूप में आदमी पृष्ठों में दी गई है

-t fd यह सच है अगर फ़ाइल डिस्क्रिप्टर fd खुला है और एक टर्मिनल को संदर्भित करता है।

... जहाँ fdसामान्य फ़ाइल डिस्क्रिप्टर असाइनमेंट में से एक हो सकता है:

0:     stdin  
1:     stdout  
2:     stderr

1
@ केल्विन मैन पेज स्निपेट का सुझाव है कि यह होना चाहिए, लेकिन उन फ़ाइल विवरणों को डिफ़ॉल्ट रूप से असाइन नहीं किया गया है।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

41
स्पष्ट करने के लिए, -tध्वज को POSIX में निर्दिष्ट किया गया है, और इस प्रकार किसी भी POSIX- संगत शेल के लिए काम करना चाहिए (अर्थात यह बैश एक्सटेंशन नहीं है)। pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
FireFly

काम करता है जब एक स्क्रिप्ट ssh दूरस्थ कमांड के रूप में अच्छी तरह से चल रहा है। सबसे अच्छा जवाब कभी और बहुत सरल।
linux_newbie 19

मैं मानता हूं कि आपके संपादन (संशोधन 5) के बाद, उत्तर संशोधन 3 की तुलना में स्पष्ट है और तथ्यात्मक रूप से सही भी है (यह देखते हुए कि "रिटर्न" का उपयोग अनौपचारिक रूप से किया जाता है जहां "प्रिंट" अधिक सटीक होगा)।
पालेक

एक के लिए देख रहा था fishखोल जवाब। उपयोग करना testसाफ-सुथरा है, लेकिन मैं इसका उदाहरण नहीं दे सकता क्योंकि यह समर्थित नहीं है। इसे एक अनुरूप में लपेटने की कोशिश की begin; ...; end, लेकिन वह काम नहीं करता था, और बस फिर से सकारात्मक कोड ब्लॉक चलाया। सोचा कि मुझे उपयोग करने की आवश्यकता हो सकती है statusलेकिन यह पाइपिंग की जांच नहीं करता है। मुझे लगता है कि मैं अनिवार्य रूप से जांचना चाहता हूं कि क्या पूर्ववर्ती आदेश / स्क्रिप्ट का STDOUT टर्मिनल पर सेट नहीं है, इन स्पष्ट उत्तरों के लिए धन्यवाद।
Pysis

126

यह निर्धारित करने का कोई मूर्खतापूर्ण तरीका नहीं है कि आपकी स्क्रिप्ट से STDIN, STDOUT, या STDERR को पाइप किया जा रहा है, मुख्यतः जैसे कार्यक्रमों के कारण ssh

चीजें जो "सामान्य रूप से" काम करती हैं

उदाहरण के लिए, निम्नलिखित बैश समाधान एक इंटरैक्टिव शेल में सही ढंग से काम करता है:

[[ -t 1 ]] && \
    echo 'STDOUT is attached to TTY'

[[ -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a pipe'

[[ ! -t 1 && ! -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a redirection'

लेकिन वे हमेशा काम नहीं करते

हालांकि, जब इस कमांड को नॉन-टीटीवाई कमांड के रूप में निष्पादित किया जाता है ssh, तो एसटीडी स्ट्रीम हमेशा ऐसा लगता है जैसे उन्हें पाइप किया जा रहा है। इसे प्रदर्शित करने के लिए, STDIN का उपयोग करना क्योंकि यह आसान है:

# CORRECT: Forced-tty mode correctly reports '1', which represents
# no pipe.
ssh -t localhost '[[ -p /dev/stdin ]]; echo ${?}'

# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports '0', which represents a pipe.
ssh -t localhost 'echo hi | [[ -p /dev/stdin ]]; echo ${?}'

# INCORRECT: Non-tty mode reports '0', which represents a pipe,
# even though one isn't specified here.
ssh -T localhost '[[ -p /dev/stdin ]]; echo ${?}'

यह क्यों मायने रखता है

यह एक बहुत बड़ी बात है, क्योंकि इसका तात्पर्य है कि एक बैश स्क्रिप्ट के लिए यह बताने का कोई तरीका नहीं है कि एक गैर-टट्टी sshकमांड को पाइप किया जा रहा है या नहीं। ध्यान दें कि यह दुर्भाग्यपूर्ण व्यवहार तब पेश किया गया था जब हाल के संस्करणों sshने गैर-टीटीवाई STDIO के लिए पाइप का उपयोग करना शुरू कर दिया था। पूर्व संस्करणों में सॉकेट्स का उपयोग किया जाता था, जिसे COULD का उपयोग करके बैश के भीतर से विभेदित किया जाता है [[ -S ]]

जब यह मायने रखता है

यह सीमा आम तौर पर समस्याओं का कारण बनती है जब आप एक बश स्क्रिप्ट लिखना चाहते हैं जिसमें संकलित उपयोगिता के समान व्यवहार होता है, जैसे cat। उदाहरण के लिए, catविभिन्न इनपुट स्रोतों को एक साथ संभालने में निम्नलिखित लचीले व्यवहार की अनुमति देता है, और यह निर्धारित करने के लिए पर्याप्त स्मार्ट है कि क्या गैर-टीटीवाई या मजबूर-टीटीवाई sshका उपयोग किए बिना यह पाइप इनपुट प्राप्त कर रहा है:

ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'

आप केवल ऐसा कुछ कर सकते हैं यदि आप मज़बूती से निर्धारित कर सकते हैं कि पाइप शामिल हैं या नहीं। अन्यथा, एक कमांड निष्पादित करना जो STDIN को पढ़ता है जब पाइप या पुनर्निर्देशन से कोई इनपुट उपलब्ध नहीं होता है, तो स्क्रिप्ट को लटका दिया जाएगा और STDIN इनपुट की प्रतीक्षा की जाएगी।

अन्य चीजें जो काम नहीं करती हैं

इस समस्या को हल करने की कोशिश में, मैंने कई तकनीकों पर ध्यान दिया है जो इस समस्या को हल करने में विफल हैं, जिनमें शामिल हैं:

  • SSH पर्यावरण चर की जांच करना
  • stat/ dev / stdin फ़ाइल डिस्क्रिप्टर पर उपयोग करना
  • के माध्यम से इंटरैक्टिव मोड की जांच [[ "${-}" =~ 'i' ]]
  • ttyऔर के माध्यम से tty स्थिति की जांचtty -s
  • के sshमाध्यम से स्थिति की जांच[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]

ध्यान दें कि यदि आप एक OS का उपयोग कर रहे हैं जो /procवर्चुअल फाइलसिस्टम का समर्थन करता है , तो आपको यह निर्धारित करने के लिए STDIO के प्रतीकात्मक लिंक के बाद किस्मत हो सकती है कि क्या पाइप का उपयोग किया जा रहा है या नहीं। हालांकि, /procएक क्रॉस-प्लेटफॉर्म नहीं है, POSIX- संगत समाधान।

मैं इस समस्या को हल करने में बेहद दिलचस्प हूं, इसलिए कृपया मुझे बताएं कि क्या आप किसी अन्य तकनीक के बारे में सोचते हैं जो काम कर सकती है, अधिमानतः पोसिक्स आधारित समाधान जो लिनक्स और बीएसडी दोनों पर काम करते हैं।


2
स्पष्ट रूप से पर्यावरण चर या प्रक्रिया के नामों का निरीक्षण बहुत अविश्वसनीय उत्तराधिकारी हैं। लेकिन क्या आप थोड़ा विस्तार कर सकते हैं कि अन्य उद्देश्य इस उद्देश्य के लिए अयोग्य हैं या उनकी समस्या क्या है? उदाहरण के लिए, मैं stat/ dev / stdin पर कॉल के आउटपुट में कोई अंतर नहीं देखता । और क्यों काम करता है "${-}"या tty -sनहीं? मैंने सोर्स कोड भी देखा, catलेकिन यह देखने में असफल रहा कि कौन सा हिस्सा वहां जादू कर रहा है जो आप पोसिक्स शेल में नहीं कर सकते। क्या आप उसका और वर्णन कर सकते हैं?
जॉस

30

कमांड test(बिलिन इन bash) के पास यह जांचने का विकल्प है कि फाइल डिस्क्रिप्टर एक ट्टी है या नहीं।

if [ -t 1 ]; then
    # stdout is a tty
fi

" man test" या " man bash" देखें और " -t" खोजें


3
+1 "मैन टेस्ट" के लिए क्योंकि / usr / बिन / परीक्षण एक शेल में भी काम करेगा जो इसके निर्माण में परीक्षण में लागू नहीं होता है
नील मैय्यू

4
Dmckee के उत्तर में FireFly द्वारा उल्लेख किया गया है, एक शेल जो लागू नहीं करता है, वह POSIX के अनुरूप नहीं है।
scy

बैश के बिलिन help test(और help helpअधिक के लिए) देखें, फिर info bashअधिक जानकारी के लिए। यदि आप कभी भी ऑफ़लाइन स्क्रिप्टिंग को समाप्त करते हैं या केवल एक व्यापक समझ प्राप्त करना चाहते हैं, तो ये आदेश बहुत अच्छे हैं।
जोएल पुर्रा

13

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

#!/bin/bash

if [[ -t 1 ]]; then
    # stdout is a terminal
else
    # stdout is not a terminal
fi

6

सोलारिस पर, देजय क्लेटन का सुझाव ज्यादातर काम करता है। -P वांछित के रूप में प्रतिक्रिया नहीं करता है।

bash_redir_test.sh जैसा दिखता है:

[[ -t 1 ]] && \
    echo 'STDOUT is attached to TTY'

[[ -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a pipe'

[[ ! -t 1 && ! -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a redirection'

लिनक्स पर, यह बहुत अच्छा काम करता है:

:$ ./bash_redir_test.sh
STDOUT is attached to TTY

:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe

:$ rm bash_redir_test.log 
:$ ./bash_redir_test.sh >> bash_redir_test.log

:$ tail bash_redir_test.log 
STDOUT is attached to a redirection

सोलारिस पर:

:# ./bash_redir_test.sh
STDOUT is attached to TTY

:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection

:# rm bash_redir_test.log 
bash_redir_test.log: No such file or directory

:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log 
STDOUT is attached to a redirection

:# 

दिलचस्प है, मेरी इच्छा है कि मेरे पास परीक्षण करने के लिए सोलारिस तक पहुंच थी। यदि आपका सोलारिस उदाहरण "/ proc" फाइलसिस्टम का उपयोग करता है, तो अधिक विश्वसनीय समाधान हैं जो स्टड, स्टडआउट और स्टडर के लिए "/ proc" प्रतीकात्मक लिंक की खोज करना शामिल हैं।
देजय क्लेटन

1

निम्नलिखित कोड (केवल लिनक्स बैश 4.4 में परीक्षण किया गया) को पोर्टेबल नहीं और न ही अनुशंसित माना जाना चाहिए , लेकिन पूर्णता के लिए यह निम्न है:

ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags: 00$' /proc/$$/fdinfo/0 && echo "pipe detected"

मुझे पता नहीं क्यों, लेकिन ऐसा लगता है कि फ़ाइल डिस्क्रिप्टर "3" किसी तरह तब बनाया जाता है जब एक बश फ़ंक्शन में स्टैडिन पाइप होता है।

आशा करता हूँ की ये काम करेगा,

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