यदि पाइप खाली है और यह नहीं है तो डेटा पर कमांड कैसे चलाएं, इसकी जांच कैसे करें?


42

मैंने बैश स्क्रिप्ट में एक लाइन डाली है और यह जाँचना चाहता हूं कि क्या पाइप में डेटा है, इसे प्रोग्राम में फीड करने से पहले।

खोज के बारे में मैंने पाया, test -t 0लेकिन यह यहाँ काम नहीं करता है। हमेशा झूठा लौटता है। तो कैसे सुनिश्चित करें कि पाइप में डेटा है?

उदाहरण:

echo "string" | [ -t 0 ] && echo "empty" || echo "fill"

आउटपुट: fill

echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"

आउटपुट: fill

मानक / विहित तरीके के विपरीत परीक्षण करना कि क्या पूर्वगामी पाइपलाइन उत्पादन का उत्पादन करती है? कार्यक्रम में इसे पास करने के लिए इनपुट को संरक्षित करने की आवश्यकता है। यह सामान्य करता है कि आउटपुट को एक प्रक्रिया से दूसरी प्रक्रिया में कैसे पाइप करें, लेकिन केवल तभी निष्पादित करें यदि पहला आउटपुट है? जो ईमेल भेजने पर केंद्रित है।


जवाबों:


37

आमतौर पर उपलब्ध शेल उपयोगिताओं का उपयोग करके पाइप की सामग्री पर झांकने का कोई तरीका नहीं है, और न ही पाइप के चरित्र को पढ़ने का कोई तरीका है, फिर इसे वापस रख दें। यह जानने का एकमात्र तरीका है कि एक पाइप में डेटा है एक बाइट को पढ़ने के लिए, और फिर आपको उस बाइट को उसके गंतव्य तक पहुंचाना होगा।

तो बस इतना करें: एक बाइट पढ़ें; यदि आप फ़ाइल के अंत का पता लगाते हैं, तो इनपुट खाली होने पर आप क्या करना चाहते हैं; यदि आप एक बाइट पढ़ते हैं, तो कांटा आप क्या करना चाहते हैं जब इनपुट खाली नहीं है, तो उस बाइट को पाइप करें, और बाकी डेटा को पाइप करें।

first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-9)
if [ -z "$first_byte" ]; then
  # stuff to do if the input is empty
else
  {
    printf "\\$first_byte"
    cat
  } | {
    # stuff to do if the input is not empty
  }      
fi

ifneसे उपयोगिता जॉय हेस के moreutils एक आदेश चलता है, तो अपने इनपुट खाली नहीं है। यह आमतौर पर डिफ़ॉल्ट रूप से स्थापित नहीं होता है, लेकिन यह अधिकांश यूनिक्स वेरिएंट पर उपलब्ध या आसान होना चाहिए। यदि इनपुट खाली है, ifneतो कुछ भी नहीं करता है और स्थिति 0 को लौटाता है, जिसे सफलतापूर्वक चलने वाले कमांड से अलग नहीं किया जा सकता है। यदि आप कुछ करना चाहते हैं यदि इनपुट खाली है, तो आपको 0 नहीं लौटने के लिए कमांड की व्यवस्था करने की आवश्यकता है, जो कि सफलता के मामले को एक अलग त्रुटि स्थिति देकर वापस किया जा सकता है:

ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
  0) echo empty;;
  255) echo success;;
  *) echo failure;;
esac

test -t 0इससे कोई लेना-देना नहीं है; यह परीक्षण करता है कि मानक इनपुट एक टर्मिनल है या नहीं। यह किसी भी तरह से या किसी भी तरह से नहीं कहता है कि क्या कोई इनपुट उपलब्ध है।


STREAMS आधारित पाइप (Solaris HP / UX) के साथ सिस्टम पर, मेरा मानना ​​है कि आप I_PEEK ioctl का उपयोग करके बिना पाइप का उपयोग किए इसे देख सकते हैं।
स्टीफन चेजलस

@ स्टीफनचेलजेलस दुर्भाग्य से * बीएसडी पर एक पाइप / फेनो से डेटा प्रवाहित करने का कोई तरीका नहीं है, इसलिए पोर्टेबल peek उपयोगिता को लागू करने के लिए कोई परिप्रेक्ष्य नहीं है जो पाइप से वास्तविक डेटा को वापस कर सकता है, न कि केवल इसमें कितना। (4.4 BSD में, 386BSD आदि पाइपों को सॉकेट जोड़े के रूप में लागू किया गया था, लेकिन यह बाद के संस्करणों में * BSD - हालांकि उन्हें द्वि-दिशात्मक रखा गया था) में जोड़ा गया था।
22

बैश इनपुट के माध्यम से उजागर करने के लिए एक दिनचर्या हैread -t 0 (इस मामले में टी का मतलब टाइमआउट है, यदि आप आश्चर्य करते हैं)।
इसहाक

11

एक सरल समाधान ifneकमांड का उपयोग करना है (यदि इनपुट खाली नहीं है)। कुछ वितरणों में, यह डिफ़ॉल्ट रूप से स्थापित नहीं है। यह moreutilsअधिकांश डिस्ट्रोस में पैकेज का एक हिस्सा है ।

ifne एक दिया कमांड चलाता है अगर और केवल अगर मानक इनपुट खाली नहीं है

ध्यान दें कि यदि मानक इनपुट खाली नहीं है, तो यह ifneदिए गए कमांड से होकर गुजरा है


2
2017 तक, यह मैक या उबंटू में डिफ़ॉल्ट रूप से नहीं है।
श्रीधर सरनोबत

7

पुराना सवाल है, लेकिन अगर कोई ऐसा करता है जैसा मैंने किया: मेरा समाधान टाइमआउट के साथ पढ़ना है।

while read -t 5 line; do
    echo "$line"
done

यदि stdinखाली है, तो यह 5 सेकंड के बाद वापस आ जाएगा। अन्यथा यह सभी इनपुट को पढ़ेगा और आप इसे आवश्यकतानुसार प्रोसेस कर सकते हैं।


जबकि मुझे यह विचार पसंद है, -tयह दुख की बात है कि वह POSIX का हिस्सा नहीं है: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
JepZ

6

जांच करें कि क्या स्टडिन (0) की फाइल डिस्क्रिप्टर खुली या बंद है:

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"

जब आप कुछ डेटा पास करते हैं और आप चेक करना चाहते हैं कि क्या कुछ है, तो आप एफडी को वैसे भी पास करते हैं, इसलिए यह भी एक अच्छा परीक्षण नहीं है।
जकूजी

1
[ -t 0 ]जाँच करता है अगर fd 0 को एक ट्टी में खोला जाता है , न कि यह बंद या खुला है।
शाम

@mosvy आप विस्तार से बता सकते हैं कि स्क्रिप्ट में उस समाधान का उपयोग कैसे प्रभावित करेगा? क्या ऐसे मामले हैं जब यह काम नहीं करता है?
जेपीजेड

@ जेपीजेड हुह? ./that_script </dev/null=> "स्टडिन में डेटा है"। या ./that_script <&-वास्तव में बंद हो गया है
मॉसवी

5

आप test -s /dev/stdin(एक स्पष्ट उपखंड में) का उपयोग कर सकते हैं ।

# test if a pipe is empty or not
echo "string" | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

echo "string" | tail -n+2 | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

: | (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

8
मेरे लिए काम नहीं करता है। हमेशा कहते हैं कि पाइप खाली है।
एम्फ़ैटेमाचिन

2
मेरे मैक पर काम करता है, लेकिन मेरे लिनक्स बॉक्स पर नहीं।
चोटी

3

बैश में:

read -t 0 

पता लगाता है कि किसी इनपुट में डेटा है (बिना कुछ पढ़े)। तब आप इनपुट पढ़ सकते हैं (यदि इनपुट उस समय उपलब्ध होता है जब रीड निष्पादित होता है):

if     read -t 0
then   read -r input
       echo "got input: $input"
else   echo "No data to read"
fi

नोट: समझें कि यह समय पर निर्भर करता है। यह पता लगाता है कि इनपुट के पास पहले से ही केवल समय पर डेटा है read -t

उदाहरण के लिए, साथ

{ sleep 0.1; echo "abc"; } | read -t 0; echo "$?"

आउटपुट है 1(विफलता पढ़ें, यानी: खाली इनपुट)। इको कुछ डेटा लिखता है, लेकिन इसकी पहली बाइट शुरू करने और लिखने के लिए बहुत तेज़ नहीं है, इस प्रकार, read -t 0रिपोर्ट करेगा कि इसका इनपुट खाली है, क्योंकि प्रोग्राम ने अभी तक कुछ भी नहीं लिखा है।


github.com/bminor/bash/blob/… - यहाँ कैसे बैश का पता चलता है कि कुछ फ़ाइल डिस्क्रिप्टर में है।
पावेल पैटरिन

धन्यवाद @PavelPatrin
आइजैक

1
@PavelPatrin जो काम नहीं करता है । जैसा कि आपके लिंक से स्पष्ट रूप से देखा जाता है, bashयह या तो उनमें से एक select()या दोनों में से कोई एक ioctl(FIONREAD)करेगा, लेकिन दोनों के लिए नहीं, जैसा कि इसके लिए काम करना चाहिए। read -t0टूट गया है। इसका उपयोग न करें
18

ऊह, आज मैं यह समझने की कोशिश करता हूं कि दो घंटे तक इसमें क्या खराबी है! धन्यवाद, @mosvy!
पावेल पैट्रिन

3

यूनिक्स में पढ़ने के लिए डेटा उपलब्ध है या नहीं, यह जांचने का एक आसान तरीका FIONREADioctl है।

मैं किसी भी मानक उपयोगिता के बारे में सोच भी नहीं सकता, इसलिए यहां एक तुच्छ कार्यक्रम कर रहा है (अधिक ifneसे अधिक आईएमएलएचओ ;-) से बेहतर )।

fionread [ prog args ... ]

यदि स्टडिन पर कोई डेटा उपलब्ध नहीं है, तो यह स्थिति 1 से बाहर निकल जाएगा। यदि डेटा है, तो यह चलेगा prog। यदि नहीं progदिया गया है, तो यह स्थिति 0 से बाहर निकल जाएगा।

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

fionread.c

#include <unistd.h>
#include <poll.h>
#include <sys/ioctl.h>
#ifdef __sun
#include <sys/filio.h>
#endif
#include <err.h>

int main(int ac, char **av){
        int r; struct pollfd pd = { 0, POLLIN };
        if(poll(&pd, 1, -1) < 0) err(1, "poll");
        if(ioctl(0, FIONREAD, &r)) err(1, "ioctl(FIONREAD)");
        if(!r) return 1;
        if(++av, --ac < 1) return 0;
        execvp(*av, av);
        err(1, "execvp %s", *av);
}

क्या यह कार्यक्रम वास्तव में काम करता है? क्या आपको किसी मामले की प्रतीक्षा करने की जरूरत नहीं है और POLLHUPसाथ ही खाली मामले को संभालने की जरूरत है? यदि पाइप के दूसरे छोर पर कई फ़ाइल विवरण हैं तो क्या यह काम करता है?
गिल्स एसओ- बुराई को रोकना '29

हाँ यह काम करता है। POLLHUP केवल पोल द्वारा लौटाया जाता है, आपको POLLHUP की प्रतीक्षा करने के लिए POLLIN का उपयोग करना चाहिए। इससे कोई फर्क नहीं पड़ता कि पाइप के किसी भी छोर पर कितने खुले हैंडल हैं।
mosvy

देखें unix.stackexchange.com/search?q=FIONREAD+user%3A22565 पर्ल से फिओरेड कैसे चलाएं (कंपाइलर की तुलना में अधिक उपलब्ध)
स्टीफन चेज़लस

FIONREAD (या HAVE_SELECT) का उपयोग करने वाली दिनचर्या को यहां bash में लागू किया गया है
इसहाक

2

यदि आपको लघु और गूढ़ एक लाइनर पसंद हैं:

$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty

मैंने मूल प्रश्न से उदाहरणों का उपयोग किया। यदि आप -qgrep के साथ पाइप्ड डेटा उपयोग विकल्प नहीं चाहते हैं


0

यदि आप पूरी पहली पंक्ति को पढ़ने के साथ ठीक हैं तो यह बैश में एक उचित ifne कार्यान्वयन प्रतीत होता है

ifne () {
        read line || return 1
        (echo "$line"; cat) | eval "$@"
}


echo hi | ifne xargs echo hi =
cat /dev/null | ifne xargs echo should not echo

5
readयदि इनपुट नॉन-खाली है तो भी गलत वापस आएगा लेकिन इसमें कोई न्यूलाइन कैरेक्टर नहीं है, readइसके इनपुट पर कुछ प्रोसेसिंग करता है और जब तक आप इसे नहीं कहते तब तक एक से अधिक लाइन पढ़ सकते हैं IFS= read -r lineechoमनमाने डेटा के लिए इस्तेमाल नहीं किया जा सकता है।
स्टीफन चेज़लस

0

यह मेरे लिए काम करता है read -rt 0

मूल प्रश्न से उदाहरण, बिना डेटा के:

echo "string" | tail -n+2 | if read -rt 0 ; then echo has data ; else echo no data ; fi

नहीं, यह काम नहीं करता है। { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }(झूठी नकारात्मक) और true | { sleep .1; read -rt0 && echo YES; }(झूठी सकारात्मक) के साथ प्रयास करें । वास्तव में, बैश है केवल लेखन मोड readमें खोले गए एफडी द्वारा भी मूर्ख बनाया जाएगा :। केवल एक चीज जो यह प्रतीत होती है, वह उस fd पर है। { read -rt0 && echo YES; cat; } 0>/tmp/fooselect(2)
mosvy

... और selectअगर read(2)वह इसे तैयार नहीं करेगा, तो कोई "वापसी" के रूप में एक एफडी लौटाएगा , चाहे वह वापस लौटे EOFया कोई त्रुटि हो। निष्कर्ष: read -t0है टूट में bash। इसका उपयोग न करें।
मच्छी

@mosvy क्या आपने इसे बैशबग के साथ रिपोर्ट किया है?
इसहाक

@mosvy { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }एक गलत नकारात्मक नहीं है क्योंकि (जिस समय रीड को निष्पादित किया जाता है) कोई इनपुट नहीं होता है। बाद में (नींद .1) कि इनपुट है (बिल्ली के लिए) उपलब्ध।
इसहाक

@mosvy r विकल्प का पता लगाने पर असर क्यों पड़ता है ?: echo "" | { read -t0 && echo YES; }YES प्रिंट echo "" | { read -rt0 && echo YES; }करता है लेकिन नहीं।
इसहाक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.