बिल्ली एक्स >> एक्स लूप क्यों करता है?


17

निम्नलिखित बैश कमांड एक इनफ़िन्ट लूप में जाते हैं:

$ echo hi > x
$ cat x >> x

मैं अंदाजा लगा सकता हूं कि इसके बाद catसे पढ़ना जारी है x। हालांकि, भ्रामक यह है कि बिल्ली का मेरा खुद का परीक्षण कार्यान्वयन अलग व्यवहार प्रदर्शित करता है:

// mycat.c
#include <stdio.h>

int main(int argc, char **argv) {
  FILE *f = fopen(argv[1], "rb");
  char buf[4096];
  int num_read;
  while ((num_read = fread(buf, 1, 4096, f))) {
    fwrite(buf, 1, num_read, stdout);
    fflush(stdout);
  }

  return 0;
}

अगर मैं चला:

$ make mycat
$ echo hi > x
$ ./mycat x >> x

यह लूप नहीं करता है। के व्यवहार catऔर इस तथ्य को देखते हुए कि मैं stdoutपहले freadसे फिर से आ रहा हूं, मुझे फिर से बुलाया जाएगा, मैं इस सी कोड को एक चक्र में पढ़ना और लिखना जारी रखने की उम्मीद करूंगा।

ये दो व्यवहार कैसे सुसंगत हैं? क्या तंत्र बताता है कि catउपरोक्त कोड क्यों नहीं चलता है?


यह मेरे लिए लूप करता है। क्या आपने इसे स्ट्रेस / ट्रस के तहत चलाने की कोशिश की है? आप किस सिस्टम पर हैं?
स्टीफन चेजलस

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

मैं डार्विन का उपयोग कर रहा हूं। मुझे वह विचार पसंद है जो cat x >> xएक त्रुटि का कारण बनता है; हालाँकि, इस आदेश को एक अभ्यास के रूप में कर्निघन और पाइक की यूनिक्स पुस्तक में सुझाया गया है।
टायलर

3
catstdio के बजाय सिस्टम कॉल का सबसे अधिक उपयोग किया जाता है। Stdio के साथ, आपका प्रोग्राम EOFness को कैशिंग कर सकता है। यदि आप 4096 बाइट्स से बड़ी फ़ाइल के साथ शुरू करते हैं, तो क्या आपको एक अनंत लूप मिलता है?
मार्क प्लॉटनिक

@MarkPlotnick, हाँ! जब फ़ाइल 4k से अधिक हो तो C कोड लूप हो जाता है। धन्यवाद, शायद यही पूरा अंतर है।
टायलर

जवाबों:


12

एक पुराने RHEL प्रणाली मुझे मिल गया है पर, /bin/catकरता नहीं के लिए पाश cat x >> xcatत्रुटि संदेश "बिल्ली: x: इनपुट फ़ाइल आउटपुट फ़ाइल है" देता है। मैं मूर्ख कर सकते हैं /bin/catऐसा करने से: cat < x >> x। जब मैं आपके कोड को ऊपर की कोशिश करता हूं, तो मुझे आपके द्वारा वर्णित "लूपिंग" मिलता है। मैंने "बिल्ली" पर आधारित एक सिस्टम कॉल भी लिखा है:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int
main(int ac, char **av)
{
        char buf[4906];
        int fd, cc;
        fd = open(av[1], O_RDONLY);
        while ((cc = read(fd, buf, sizeof(buf))) > 0)
                if (cc > 0) write(1, buf, cc);
        close(fd);
        return 0;
}

यह छोरों, भी। यहाँ केवल बफ़रिंग (stdio- आधारित "mycat" के लिए) कर्नेल में क्या होता है।

मुझे लगता है कि क्या हो रहा है कि फाइल डिस्क्रिप्टर 3 (परिणाम open(av[1])) की फाइल में एक ऑफसेट है। 0. डिस्क्रिप्टर 1 (स्टडआउट) में 3 की ऑफसेट है, क्योंकि ">>" इनवॉइसिंग शेल को lseek()ऑन करने का कारण बनता है । फ़ाइल डिस्क्रिप्टर को catचाइल्ड प्रोसेस को सौंपने से पहले ।

read()किसी भी प्रकार का एक करना , चाहे एक stdio बफर में, या एक सादा char buf[]फ़ाइल डिस्क्रिप्टर write()की स्थिति को आगे बढ़ाता है 3. एक एडवांस फ़ाइल डिस्क्रिप्टर की स्थिति को 1 कर रहा है। वे दो ऑफ़सेट अलग-अलग संख्याएँ हैं। ">>" के कारण, फ़ाइल डिस्क्रिप्टर 1 में हमेशा फ़ाइल डिस्क्रिप्टर 3 की ऑफ़सेट की तुलना में अधिक या एक ऑफ़सेट होता है। इसलिए कोई भी "कैट-लाइक" प्रोग्राम लूप करेगा, जब तक कि यह कुछ आंतरिक बफरिंग न करे। यह संभव है, शायद यह भी संभावना है कि, FILE *(जो कि प्रतीकों का प्रकार stdoutऔर fआपके कोड में) का एक stdio कार्यान्वयन है जिसमें अपना स्वयं का बफर शामिल है। fread()वास्तव read()में आंतरिक बफर के लिए एक सिस्टम कॉल कर सकते हैं f। के इनसाइड में यह कुछ भी बदल सकता है या नहीं भी stdoutfwrite()पर बुला रहा हैstdoutअंदर कुछ भी बदल सकता है या नहीं भी f। तो एक stdio- आधारित "बिल्ली" लूप नहीं हो सकता है। या यह हो सकता है। बहुत बदसूरत, बदसूरत libc कोड के माध्यम से पढ़ने के बिना कहना मुश्किल है।

मैंने straceआरएचईएल पर किया था cat- यह सिर्फ read()और write()सिस्टम कॉल का उत्तराधिकार करता है । लेकिन catइस तरह से काम करने की जरूरत नहीं है। यह mmap()इनपुट फ़ाइल के लिए संभव होगा , तब करें write(1, mapped_address, input_file_size)। कर्नेल सभी काम करेगा। या आप sendfile()लिनक्स सिस्टम पर इनपुट और आउटपुट फाइल डिस्क्रिप्टर के बीच एक सिस्टम कॉल कर सकते हैं । पुराने SunOS 4.x सिस्टम को मेमोरी मैपिंग ट्रिक करने के लिए अफवाह थी, लेकिन मुझे नहीं पता कि किसी ने कभी भी सेंडफाइल-आधारित बिल्ली की है। या तो मामले में "लूपिंग" नहीं होगा, दोनों के रूप में write()और sendfile()लंबाई-से-स्थानांतरण पैरामीटर की आवश्यकता होती है।


धन्यवाद। डार्विन पर, ऐसा लगता है कि जैसे freadमार्क प्लॉटनिक ने सुझाव दिया था कि एक ईओएफ ध्वज को बंद किया गया है। साक्ष्य: [1] डार्विन बिल्ली पढ़ती है, न कि भय; और [2] डार्विन के फैडर __srefill को कॉल करते हैं जो fp->_flags |= __SEOF;कुछ मामलों में सेट होता है। [1] src.gnu-darwin.org/src/bin/cat/cat.c [2] opensource.apple.com/source/Libc/Libc-167/stdio.subproj/...
टायलर

1
यह कमाल है - मैं कल इसे सबसे पहले उखाड़ने वाला था। यह ध्यान देने योग्य बात हो सकती है कि इसके लिए एकमात्र POSIX परिभाषित स्विच catहै cat -u- unbuffered के लिए u
मोकेसर 18

वास्तव में, ध्वज के >>साथ खुला () कॉल करके लागू किया जाना चाहिए O_APPEND, जिसके कारण हर लिखने का कार्य (एटोमिकली) फ़ाइल के वर्तमान छोर पर लिखता है, इससे कोई फर्क नहीं पड़ता कि फ़ाइल विवरणक की स्थिति पढ़ने से पहले क्या थी। यह व्यवहार foo >> logfile & bar >> logfileसही ढंग से काम करने के लिए आवश्यक है, उदाहरण के लिए - आप यह मानने का जोखिम नहीं उठा सकते हैं कि आपके स्वयं के अंतिम लेखन के बाद की स्थिति अभी भी फ़ाइल का अंत है।
हमाखोल ने

1

एक आधुनिक बिल्ली कार्यान्वयन (सनोस-4.0 1988) पूरी फ़ाइल को मैप करने के लिए mmap () का उपयोग करता है और फिर इस स्थान के लिए 1x राइट () कॉल करता है। जब तक वर्चुअल मेमोरी पूरी फ़ाइल को मैप करने की अनुमति देती है, तब तक ऐसा कार्यान्वयन लूप नहीं होगा।

अन्य कार्यान्वयन के लिए यह इस बात पर निर्भर करता है कि फ़ाइल I / O बफर से बड़ी है या नहीं।


कई catकार्यान्वयन अपने आउटपुट ( -uनिहित) को बफर नहीं करते हैं । वे हमेशा लूप करेंगे।
स्टीफन चेजलस

Solaris 11 (SunOS-5.11) छोटी फ़ाइलों के लिए mmap () का उपयोग नहीं करता प्रतीत होता है (केवल 32769 बाइट्स बड़ी या ऊपर की फ़ाइलों के लिए इसका सहारा लेता है)।
स्टीफन चेजलस

सही -u आमतौर पर डिफ़ॉल्ट है। यह एक लूप का मतलब नहीं है क्योंकि एक कार्यान्वयन पूरे फाइल को पढ़ सकता है और उस बफ़ के साथ सिर्फ एक लिख सकता है।
21

Solaris कैट केवल लूप्स करता है यदि फाइलेज है> अधिकतम मैप करता है या यदि प्रारंभिक फाइलऑफसेट है! = 0.
schily

मैं सोलारिस 11 के साथ क्या निरीक्षण करता हूं। यदि प्रारंभिक ऑफसेट है तो यह एक रीड () लूप करता है! = 0 या यदि फाइल में betwen 0 और 32768 है। इससे ऊपर, यह mmaps () 8MiB फाइल के बड़े क्षेत्रों को एक बार में और कभी नहीं करता है। वापस पढ़ने के लिए लगता है () PiB फ़ाइलों के लिए भी छोरों (विरल फ़ाइलों पर परीक्षण)।
स्टीफन चेज़लस

0

जैसा कि बैश नुकसान में लिखा गया है , आप एक फ़ाइल से नहीं पढ़ सकते हैं और उसी पाइपलाइन में लिख सकते हैं।

आपकी पाइप लाइन क्या करती है, इस पर निर्भर करते हुए, फ़ाइल को बंद किया जा सकता है (0 बाइट्स तक, या संभवतः आपके ऑपरेटिंग सिस्टम के पाइपलाइन बफर के आकार के बराबर बाइट्स की संख्या), या यह तब तक बढ़ सकता है जब तक यह उपलब्ध डिस्क स्थान को भरता है, या पहुंचता है आपके ऑपरेटिंग सिस्टम की फ़ाइल का आकार सीमा, या आपका कोटा, आदि।

समाधान या तो पाठ संपादक, या अस्थायी चर का उपयोग करना है।


-1

आप दोनों के बीच किसी तरह की दौड़ की स्थिति है x। के कुछ कार्यान्वयन cat(जैसे कि कोर्यूटिल्स 8.23) निषिद्ध हैं:

$ cat x >> x
cat: x: input file is output file

यदि यह पता नहीं लगाया गया है, तो व्यवहार स्पष्ट रूप से कार्यान्वयन (बफर आकार, आदि) पर निर्भर करेगा।

आपके कोड में, यदि एंड-ऑफ़-फ़ाइल संकेतक सेट किया clearerr(f);गया है fflush, तो अगली बार freadत्रुटि होने पर , आप उसके बाद जोड़ने का प्रयास कर सकते हैं ।


ऐसा लगता है कि एक अच्छे OS के पास एक एकल प्रक्रिया के लिए नियतात्मक व्यवहार होगा, जिसमें एक ही थ्रेड एक ही रीड / राइट कमांड चलाएगा। किसी भी मामले में, व्यवहार मेरे लिए निर्धारक है, और मैं मुख्य रूप से विसंगति के बारे में पूछ रहा हूं।
टायलर

@ टायलर IMHO, इस मामले पर स्पष्ट विनिर्देश के बिना, ऊपर दिए गए आदेश का कोई मतलब नहीं है, और निर्धारकवाद वास्तव में महत्वपूर्ण नहीं है (यहां एक त्रुटि को छोड़कर, जो सबसे अच्छा व्यवहार है)। यह सी के i = i++;अपरिभाषित व्यवहार की तरह एक सा है, इसलिए विसंगति है।
vinc17

1
नहीं, यहां कोई दौड़ की स्थिति नहीं है, व्यवहार अच्छी तरह से परिभाषित है। हालांकि यह कार्यान्वयन-परिभाषित है, जो फ़ाइल के सापेक्ष आकार और उपयोग किए गए बफर पर निर्भर करता है cat
गिल्स एसओ- बुराई को रोकें '

@Gilles आप कहाँ देखते हैं कि व्यवहार अच्छी तरह से परिभाषित / कार्यान्वयन-परिभाषित है? क्या आप कुछ संदर्भ दे सकते हैं? POSIX बिल्ली विनिर्देश बस कहता है: "यह कार्यान्वयन-परिभाषित है कि क्या बिल्ली उपयोगिता बफ़र्स आउटपुट अगर -u विकल्प निर्दिष्ट नहीं है।" हालाँकि, जब एक बफर का उपयोग किया जाता है, तो कार्यान्वयन को यह परिभाषित करने की आवश्यकता नहीं है कि इसका उपयोग कैसे किया जाता है; यह गैर-नियतात्मक हो सकता है, उदाहरण के लिए यादृच्छिक समय पर एक बफर के साथ।
vinc17

@ vinc17 कृपया मेरी पिछली टिप्पणी में "व्यवहार में" डालें। हां, यह सैद्धांतिक रूप से संभव है और पोसिक्स-अनुरूप है, लेकिन कोई भी ऐसा नहीं करता है।
गिल्स एसओ- बुराई को रोकना '
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.