अतिरिक्त पात्रों को खाता है


15

निम्नलिखित शेल कमांड से इनपुट स्ट्रीम की केवल विषम रेखाओं के प्रिंट की उम्मीद थी:

echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)

लेकिन इसके बजाय यह सिर्फ पहली पंक्ति प्रिंट करता है aaa:।

जब इसका उपयोग -c( --bytes) विकल्प के साथ किया जाता है तो वही नहीं होता है:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)

यह कमांड 1234512345उम्मीद के मुताबिक आउटपुट करता है। लेकिन यह केवल उपयोगिता के कोर्यूटिल्स कार्यान्वयन में काम करता है headबिजीबॉक्स कार्यान्वयन अभी भी, अतिरिक्त वर्ण खाती है तो उत्पादन सिर्फ है 12345

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

जब --bytesविकल्प का उपयोग किया जाता है तो केस के लिए भी ऐसा नहीं कहा जा सकता है। इस मामले में आप जानते हैं कि आपको कितने बाइट्स पढ़ने हैं। तो आप इस बाइट की संख्या को ठीक से पढ़ सकते हैं और इससे अधिक नहीं। Corelibs कार्यान्वयन इस अवसर का उपयोग करता है, लेकिन बिजीबॉक्स एक नहीं, यह अभी भी अधिक बाइट की तुलना में एक बफर में आवश्यक पढ़ता है। यह संभवतः कार्यान्वयन को सरल बनाने के लिए किया जाता है।

तो सवाल। क्या headउपयोगिता के लिए इनपुट स्ट्रीम से अधिक पात्रों का उपभोग करना सही है, जितना कि पूछा गया था? क्या यूनिक्स उपयोगिताओं के लिए किसी प्रकार का मानक है? और अगर वहाँ है, तो क्या यह व्यवहार निर्दिष्ट करता है?

पुनश्च

आपको Ctrl+Cऊपर दिए गए कमांड्स को रोकने के लिए प्रेस करना होगा। यूनिक्स उपयोगिताओं से परे पढ़ने पर विफल नहीं होते हैं EOF। यदि आप प्रेस नहीं करना चाहते हैं, तो आप अधिक जटिल कमांड का उपयोग कर सकते हैं:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)

जो मैंने सादगी के लिए उपयोग नहीं किया।


2
निकटवर्ती unix.stackexchange.com/questions/48777/… और unix.stackexchange.com/questions/84011/… । इसके अलावा, अगर यह शीर्षक फिल्मों पर था। तो मेरा जवाब जरदोज़ होगा :)
dave_thompson_085

जवाबों:


30

क्या हेड उपयोगिता के लिए इनपुट स्ट्रीम से अधिक वर्णों का उपभोग करना सही है, जितना कि पूछा गया था?

हां, इसकी अनुमति है (नीचे देखें)।

क्या यूनिक्स उपयोगिताओं के लिए किसी प्रकार का मानक है?

हाँ, POSIX वॉल्यूम 3, शैल और उपयोगिताएँ

और अगर वहाँ है, तो क्या यह व्यवहार निर्दिष्ट करता है?

यह, इसकी शुरूआत में है:

जब एक मानक उपयोगिता एक शोध योग्य इनपुट फ़ाइल को पढ़ती है और एक त्रुटि के बिना समाप्त हो जाती है, तो यह फ़ाइल के अंत तक पहुंचने से पहले, उपयोगिता सुनिश्चित करेगी कि खुले फ़ाइल विवरण में फ़ाइल ऑफ़सेट उपयोगिता द्वारा संसाधित अंतिम बाइट से ठीक पहले स्थित है। ऐसी फ़ाइलों के लिए जो खोज योग्य नहीं हैं, फ़ाइल की स्थिति उस फ़ाइल के लिए खुले फ़ाइल विवरण में ऑफसेट होती है जो अनिर्दिष्ट है।

headमें से एक है मानक उपयोगिताओं , तो एक POSIX अनुरूप क्रियान्वयन व्यवहार ऊपर वर्णित कार्यान्वित करना होगा।

जीएनयू फ़ाइल डिस्क्रिप्टर को सही स्थिति में छोड़ने की कोशिश head करता है , लेकिन पाइपों पर तलाश करना असंभव है, इसलिए आपके परीक्षण में यह स्थिति को बहाल करने में विफल रहता है। आप इसका उपयोग करके देख सकते हैं strace:

$ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
...
read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
lseek(0, -13, SEEK_CUR)                 = -1 ESPIPE (Illegal seek)
...

readरिटर्न 17 बाइट (सभी उपलब्ध इनपुट), headउन में से चार को संसाधित करता है और फिर वापस 13 बाइट्स स्थानांतरित करने के लिए कोशिश करता है, लेकिन यह नहीं कर सकता। (आप यहां यह भी देख सकते हैं कि GNU head8 KiB बफर का उपयोग करता है।)

जब आप headबाइट्स (जो कि गैर-मानक है) को गिनने के लिए कहते हैं, तो यह पता है कि कितने बाइट्स पढ़ने हैं, इसलिए यह (यदि उस तरह से लागू किया गया है) तो उसके अनुसार अपने रीड को सीमित करें। यही कारण है कि आपका head -c 5परीक्षण कार्य करता है: GNU headकेवल पांच बाइट्स पढ़ता है और इसलिए फ़ाइल डिस्क्रिप्टर की स्थिति को पुनर्स्थापित करने की आवश्यकता नहीं है।

यदि आप किसी फ़ाइल में दस्तावेज़ लिखते हैं, और इसके बजाय उसका उपयोग करते हैं, तो आपको वह व्यवहार मिलेगा जो आप उसके बाद करेंगे:

$ echo -e "aaa\nbbb\nccc\nddd\n" > file
$ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
aaa
ccc

2
एक line(अब POSIX / XPG से हटा दिया गया है, लेकिन अभी भी कई प्रणालियों पर उपलब्ध है) या read( IFS= read -r line) उपयोगिताओं के बजाय उपयोग कर सकते हैं जो समस्या से बचने के लिए एक बार में एक बाइट पढ़ते हैं।
स्टीफन चेज़लस

3
ध्यान दें कि क्या head -c 55 बाइट्स पढ़ेंगे या एक पूर्ण बफर कार्यान्वयन पर निर्भर करता है (यह भी ध्यान दें कि head -cमानक नहीं है), आप उस पर भरोसा नहीं कर सकते। आपको इस बात की dd bs=1 count=5गारंटी होगी कि 5 से अधिक बाइट्स नहीं पढ़े जाएंगे।
स्टीफन चेज़लस

धन्यवाद @ स्टीफन, मैंने -c 5विवरण अपडेट कर दिया है ।
स्टीफन किट

ध्यान दें कि जब इनपुट संभव नहीं होता है, तो एक बार में headनिर्मित ksh93एक बाइट पढ़ता है head -n 1
स्टीफन चेज़लस

1
@anton_rh, ddकेवल पाइप के साथ सही ढंग से काम करता है bs=1यदि आप countपाइप पर रीड के रूप में उपयोग करते हैं तो अनुरोध की तुलना में कम वापस आ सकते हैं (लेकिन कम से कम एक बाइट जब तक ईओएफ नहीं पहुंचता है)। GNU ddमें iflag=fullblockऐसा है कि हालांकि कम हो सकता है।
स्टीफन चेज़लस

6

POSIX से

सिर उपयोगिता मानक आउटपुट में अपने इनपुट फ़ाइलों की प्रतिलिपि जाएगा, एक निर्दिष्ट बिंदु पर एक फ़ाइल के लिए आउटपुट अंत हो गया।

यह कुछ भी नहीं कहता head है कि इनपुट से कितना पढ़ना चाहिए। इसे बाइट-बाय-बाय पढ़ने की मांग करना मूर्खतापूर्ण होगा, क्योंकि यह ज्यादातर मामलों में बेहद धीमा होगा।

हालाँकि, यह readबिल्टिन / यूटिलिटी में संबोधित किया गया है : सभी गोले मैं readपाइप से एक बार में पा सकते हैं और मानक पाठ का अर्थ यह लगाया जा सकता है कि ऐसा किया जाना चाहिए, बस एक ही पंक्ति को पढ़ने में सक्षम होने के लिए:

पढ़ने उपयोगिता एक या अधिक खोल चर में मानक इनपुट से एक भी तार्किक लाइन पढ़ा जाएगा।

के मामले में read, जो शेल स्क्रिप्ट में प्रयोग किया जाता है, एक का उपयोग आम तौर कुछ इस तरह होगा:

read someline
if something ; then 
    someprogram ...
fi

यहां, मानक इनपुट someprogramशेल के समान है, लेकिन यह उम्मीद की जा सकती है कि someprogramपहली इनपुट लाइन द्वारा उपभोग किए जाने के बाद आने वाली हर चीज को पढ़ने के लिए मिलता है readऔर बफर द्वारा पढ़े जाने के बाद जो कुछ बचा था वह नहीं read। दूसरी ओर, headअपने उदाहरण के रूप में उपयोग करना बहुत अधिक असामान्य है।


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

$ seq 1 10 | sed -ne '1~2p'   # GNU sed
$ seq 1 10 | sed -e 'n;d'     # works in GNU sed and the BSD sed on macOS

$ seq 1 10 | awk 'NR % 2' 
$ seq 1 10 | perl -ne 'print if $. % 2'

लेकिन POSIX के "INPUT FILES" खंड 3 को देखें खंड ...
स्टीफन किट

1
POSIX कहता है: "जब एक मानक उपयोगिता एक खोजी इनपुट फ़ाइल पढ़ती है और एक त्रुटि के बिना समाप्त हो जाती है, इससे पहले कि वह फ़ाइल तक पहुँच जाए, तो उपयोगिता यह सुनिश्चित करेगी कि खुले फ़ाइल विवरण में फ़ाइल ऑफसेट ठीक से संसाधित अंतिम बाइट के पिछले स्थान पर स्थित है उपयोगिता। उन फ़ाइलों के लिए, जो खोजे जाने योग्य नहीं हैं, फ़ाइल की स्थिति उस फ़ाइल के लिए खुले फ़ाइल विवरण में ऑफ़सेट है जो अनिर्दिष्ट है। "
एलेक्सा डे

2
ध्यान दें कि जब तक आप उपयोग नहीं करते हैं -r, readएक से अधिक लाइन पढ़ सकते हैं (इसके बिना IFS=लीडिंग और ट्रेलिंग स्पेस और टैब (डिफ़ॉल्ट मान $IFS) के साथ पट्टी भी हो सकती है )।
स्टीफन चेज़लस

@ AlexP, हाँ, स्टीफन ने सिर्फ उस हिस्से को जोड़ा।
13लकक्चु '

ध्यान दें कि जब इनपुट संभव नहीं होता है, तो एक बार में headनिर्मित ksh93एक बाइट पढ़ता है head -n 1
स्टीफन चेज़लस

1
awk '{if (NR%2) == 1) print;}'

Hellóka :-) और साइट पर आपका स्वागत है! ध्यान दें, हम अधिक विस्तृत जवाब पसंद करते हैं। वे भविष्य के googlers के लिए उपयोगी होना चाहिए।
पेटेर - मोनिका
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.