स्टड की अंतिम पंक्ति को पूरे स्टड पर रोकें


9

इस लिपि पर विचार करें:

tmpfile=$(mktemp)

cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS

cat <(tail -1 "$tmpfile") "$tmpfile"

यह काम करता है और आउटपुट:

line 3
line 1
line 2
line 3

मान लें कि हमारा इनपुट स्रोत, वास्तविक फ़ाइल होने के बजाय, स्टडिन था:

cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS

हम कमांड को कैसे संशोधित करते हैं:

cat <(tail -1 "$tmpfile") "$tmpfile"

ताकि यह अभी भी एक ही आउटपुट का उत्पादन करता है, इस अलग संदर्भ में?

नोट: विशिष्ट हेरेडोक जिसे मैं पूरा कर रहा हूं, साथ ही एक हेरेडोक का उपयोग केवल चित्रण है। किसी भी स्वीकार्य उत्तर को यह मान लेना चाहिए कि यह स्टड के माध्यम से मनमाना डेटा प्राप्त कर रहा है ।


1
स्टडेन हमेशा एक "वास्तविक फ़ाइल" होती है (एक फेनो / सॉकेट / आदि एक फाइल भी होती है; सभी फाइलें खोजी नहीं होती हैं)। आपके प्रश्न का उत्तर या तो एक तुच्छ "अस्थायी फ़ाइल का उपयोग करें" या कुछ डरावनी है जो पूरी फ़ाइल को मेमोरी में लोड करेगा। "मैं एक स्ट्रीम से पुराने डेटा को बिना कहीं संग्रहीत किए कैसे प्राप्त कर सकता हूं ?" एक अच्छा जवाब नहीं हो सकता।
मॉसवी

1
@mosvy यदि आप इसे जोड़ना चाहते हैं तो यह पूरी तरह स्वीकार्य उत्तर है।
योना

2
@mosvy जैसा कि योना ने कहा है, उत्तर बॉक्स में उत्तर पोस्ट किए जाने चाहिए। मुझे पता है कि इस समय किसी भी वेबसाइट को पढ़ना मुश्किल है, लेकिन कृपया लाल को अनदेखा करें जो धीरे-धीरे आपकी दृष्टि से टपकता है और निचली बनावट का उपयोग करें।
wizzwizz4

जवाबों:


7

प्रयत्न:

awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'

उदाहरण

हमारे इनपुट के साथ एक चर को परिभाषित करें:

$ input="line 1
> line 2
> line 3"

हमारे आदेश चलाएँ:

$ echo "$input" | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 3
line 1
line 2
line 3

वैकल्पिक रूप से, निश्चित रूप से, हम यहाँ एक डॉक्टर का उपयोग कर सकते हैं:

$ cat <<EOS | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 1
line 2
line 3
EOS
line 3
line 1
line 2
line 3

यह काम किस प्रकार करता है

  • x=x $0 ORS

    यह चर के लिए इनपुट की प्रत्येक पंक्ति को जोड़ता है x

    Awk में, ORSहै उत्पादन रिकॉर्ड विभाजक । डिफ़ॉल्ट रूप से, यह एक नया चरित्र है।

  • END{printf "%s", $0 ORS x}

    जब हमने पूरी फ़ाइल में पढ़ा है, तो यह अंतिम पंक्ति को प्रिंट करता है, $0और पूरी फ़ाइल की सामग्री के बाद x

चूंकि यह पूरे इनपुट को मेमोरी में पढ़ता है, इसलिए यह बड़े ( जैसे गीगाबाइट) इनपुट के लिए उपयुक्त नहीं होगा ।


धन्यवाद जॉन। तो क्या ओपी में मेरे नामांकित फ़ाइल उदाहरण के अनुरूप यह संभव नहीं है? मैं कल्पना कर रहा था कि किसी तरह से डुप्लिकेट किया जा रहा है ... जिस तरह से teeकरता है, लेकिन एक स्टडिन और एक फाइल की तरह, हम एक ही स्टड को दो अलग-अलग प्रक्रिया प्रतिस्थापनों में पाइपिंग करेंगे। या कुछ भी है कि लगभग उसी के बराबर होगा?
योना

5

यदि स्टैडेन एक साधक फ़ाइल की ओर इंगित करता है (जैसे बैश के मामले में (लेकिन अन्य सभी शेल नहीं) तो यहां दस्तावेज जो अस्थायी फाइलों के साथ लागू होते हैं), आप पूंछ प्राप्त कर सकते हैं और फिर पूरी सामग्री पढ़ने से पहले वापस आ सकते हैं:

चाहने वाले ऑपरेटर zshया ksh93गोले, या स्क्रिप्टिंग भाषा में उपलब्ध हैं जैसे कि tcl / perl / python, लेकिन अंदर नहीं bash। लेकिन आप हमेशा उन अधिक उन्नत दुभाषियों को कॉल कर सकते हैं bashयदि आपको उपयोग करना है bash

ksh93 -c 'tail -n1; cat <#((0))' <<...

या

zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...

अब, यह तब काम नहीं करेगा जब स्टड एक गैर-खोजने योग्य फ़ाइलों जैसे पाइप या सॉकेट को इंगित करता है। फिर, एकमात्र विकल्प पूरे मेमोरी में (मेमोरी में या एक अस्थायी फ़ाइल में ...) पढ़ना और स्टोर करना है।

स्मृति में भंडारण के लिए कुछ समाधान पहले से ही दिए गए हैं।

एक अस्थायी के साथ zsh, आप इसके साथ कर सकते हैं:

seq 10 | zsh -c '{ cat =(sed \$w/dev/fd/3); } 3>&1'

यदि लिनक्स पर, यहाँ bashया zshदस्तावेज़ों के लिए अस्थायी फ़ाइलों का उपयोग करने वाले किसी भी शेल के साथ , आप वास्तव में आउटपुट को संग्रहीत करने के लिए यहाँ-दस्तावेज़ द्वारा बनाई गई अस्थायी फ़ाइल का उपयोग कर सकते हैं:

seq 10 | {
  chmod u+w /dev/fd/3 # only needed in bash5+
  cat > /dev/fd/3
  tail -n1 /dev/fd/3
  cat <&3
} 3<<EOF
EOF

4
cat <<EOS | sed -ne '1{h;d;}' -e 'H;${G;p;}'
line 1
line 2
line 3
EOS

इसका उपयोग करने वाली किसी चीज़ के लिए अनुवाद करने के साथ समस्या यह tailहै कि tailइसके अंत का पता लगाने के लिए पूरी फ़ाइल को पढ़ने की आवश्यकता है। अपनी पाइपलाइन में इसका उपयोग करने के लिए, आपको इसकी आवश्यकता है

  1. दस्तावेज़ की पूरी सामग्री प्रदान करें tail
  2. इसे फिर से प्रदान करें cat
  3. उस क्रम में।

ट्रिकी बिट डॉक्यूमेंट के कंटेंट को डुप्लिकेट नहीं teeकरता ( ऐसा करता है), लेकिन tailबाकी डॉक्यूमेंट को आउटपुट करने से पहले होने वाले आउटपुट को पाने के लिए, बिना इंटरमीडिएट अस्थायी फाइल का उपयोग किए।

प्रयोग sed(या awk, जैसा कि जॉन 1024 करता है ) डेटा के दोहरे पार्सिंग और मेमोरी में डेटा को संग्रहीत करके आदेश जारी करने से छुटकारा दिलाता है

sedसमाधान है कि मैं प्रस्ताव करने के लिए है

  1. 1{h;d;}, पहली पंक्ति को होल्ड स्पेस में स्टोर करें, जैसा कि है, और अगली लाइन पर जाएं।
  2. H, एक नई लाइन के साथ पकड़ स्थान के लिए एक दूसरे को लाइन में जोड़ें।
  3. ${G;p;}, अंतिम स्थान को एक एम्बेडेड न्यूलाइन के साथ अंतिम पंक्ति में जोड़ें और परिणामी डेटा को प्रिंट करें।

यह जॉन 1024 के समाधान का काफी शाब्दिक अनुवाद है sed, इस चेतावनी के साथ कि POSIX मानक केवल यह गारंटी देता है कि पकड़ स्थान कम से कम 8192 बाइट्स (8 KiB) पर है, लेकिन यह अनुशंसा करता है कि यह बफर गतिशील रूप से आवंटित और आवश्यकतानुसार विस्तारित हो, जो दोनों GNU है। sedऔर बीएसडी sedकर रहा है)।


यदि आप अपने आप को नामित पाइप का उपयोग करने की अनुमति देते हैं:

mkfifo mypipe
cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
line 1
line 2
line 3
EOS
rm -f mypipe

यह teeडेटा को नीचे mypipeऔर उसी समय भेजने के लिए उपयोग करता है catcatउपयोगिता पहले से उत्पादन पढ़ा जाएगा tail(जिसमें से पढ़ता है mypipe, जो teeकरने के लिए लिख रहा है), और फिर दस्तावेज़ से सीधे आने की प्रतिलिपि संलग्न tee

हालांकि इसमें एक गंभीर दोष है, कि अगर दस्तावेज़ बहुत बड़ा है (पाइप के बफर आकार से बड़ा), तो teeलिखने के लिए mypipeऔर cat(अनाम) पाइप को खाली करने के लिए इंतजार करते समय ब्लॉक होगा। इसे तब तक खाली नहीं किया जाएगा जब तक catकि इसे पढ़ा नहीं जाता। समाप्त catहोने तक इसे नहीं पढ़ा जा सकेगा tail। और tailतब तक खत्म नहीं होगा जब तक खत्म नहीं teeहो जाता। यह एक क्लासिक गतिरोध की स्थिति है।

भिन्नता

tee >( tail -n 1 >mypipe ) | cat mypipe -

एक ही मुद्दा है।


2
sedयदि इनपुट केवल एक लाइन (शायद है एक काम नहीं करता है sed '1h;1!H;$!d;G')। यह भी ध्यान दें कि कई sedकार्यान्वयन में उनके पैटर्न और होल्ड स्पेस के आकार की कम सीमा होती है।
स्टीफन चेज़लस

नामित पाइप समाधान उस तरह की चीज है जिसकी मुझे तलाश थी। मर्यादा शर्म की बात है। मुझे समझ में आया कि "और पूंछ खत्म होने तक पूंछ खत्म नहीं होगी" को छोड़कर - क्या आप इस बारे में विस्तार से बता सकते हैं कि ऐसा क्यों है?
जोनाह

2

peeकमांड-लाइन उपयोगिताओं के संग्रह में नामित एक उपकरण है, जिसे आमतौर पर "अधिक जानकारी" (या अन्यथा इसकी घर की वेबसाइट से पुनर्प्राप्ति योग्य ) नाम के साथ पैक किया जाता है ।

यदि आपके पास यह आपके सिस्टम पर हो सकता है तो आपके उदाहरण के बराबर होगा:

cat <<EOS | pee 'tail -1' cat 
line 1
line 2
line 3
EOS

के माध्यम से चलाए गए आदेशों का आदेश देना peeमहत्वपूर्ण है क्योंकि वे प्रदान किए गए अनुक्रम में निष्पादित होते हैं।


1

प्रयत्न:

cat <<EOS # | what goes here now? Nothing!
line 3
line 1
line 2
line 3
EOS

चूँकि पूरी चीज़ शाब्दिक डेटा है (एक "यहाँ-दस्तावेज है"), और इसके और वांछित आउटपुट के बीच का अंतर तुच्छ है, बस उस शाब्दिक डेटा को आउटपुट से मिलान करने के लिए वहीं मालिश करें।

अब मान लीजिए कि line 3यह कहीं से आया है और इसे चर में संग्रहीत किया जाता है lastline:

cat <<EOS # | what goes here now? Nothing!
$lastline
line 1
line 2
$lastline
EOS

यहाँ एक डॉक्टर में, हम चर को प्रतिस्थापित करके पाठ उत्पन्न कर सकते हैं। इतना ही नहीं, लेकिन हम कमांड प्रतिस्थापन का उपयोग करके पाठ की गणना कर सकते हैं:

cat <<EOS
this is template text
here we have a hex conversion: $(printf "%x" 42)
EOS

हम कई लाइनों को प्रक्षेपित कर सकते हैं:

cat <<EOS
multi line
preamble
$(for x in 3 1 2 3; do echo line $x ; done)
epilog
EOS

सामान्य तौर पर, यहां डॉक टेम्प्लेट के प्रसंस्करण से बचें; प्रक्षेपित कोड का उपयोग करके इसे उत्पन्न करने का प्रयास करें।


1
मैं ईमानदारी से नहीं बता सकता कि यह मजाक है या नहीं। cat <<EOS...ओपी में सिर्फ "एक मनमाना फ़ाइल catting," पोस्ट विशिष्ट और सवाल स्पष्ट करना के लिए एक उदाहरण पर खड़ी होकर था। क्या यह वास्तव में आपके लिए स्पष्ट नहीं था, या क्या आपने सिर्फ यह सोचा था कि शाब्दिक रूप से प्रश्न की व्याख्या करना चतुर होगा?
योना

@ जोना सवाल स्पष्ट रूप से कहता है "[l] एट का कहना है कि हमारा इनपुट स्रोत, वास्तविक फ़ाइल होने के बजाय, स्टडिन था"। "मनमानी फ़ाइलों" के बारे में कुछ भी नहीं; यह डॉक्स के बारे में है। यहाँ एक डॉक्टर मनमाना नहीं है। यह आपके प्रोग्राम का इनपुट नहीं है, बल्कि इसके सिंटैक्स का एक टुकड़ा है जिसे प्रोग्रामर चुनता है।
काज

1
मुझे लगता है कि संदर्भ और मौजूदा जवाबों ने यह स्पष्ट कर दिया कि मामला यही था, यदि केवल इसलिए कि आपकी व्याख्या सही होने के लिए आपको शाब्दिक रूप से यह मानना ​​पड़ा कि न तो मैंने और न ही किसी अन्य पोस्टर ने उत्तर दिया कि यह कॉपी करना और पेस्ट करना संभव था कोड की लाइन। फिर भी, मैं इसे स्पष्ट करने के लिए प्रश्न को संपादित करूंगा।
योना

1
Kaz, उत्तर के लिए धन्यवाद, लेकिन अपने संपादन के साथ भी ध्यान दें, आप प्रश्न के इरादे को याद कर रहे हैं। आप पाइप के माध्यम से मनमाना मल्टीलाइन इनपुट प्राप्त कर रहे हैं । आपको पता नहीं है कि यह क्या होगा। आपका कार्य इनपुट की अंतिम पंक्ति का उत्पादन करना है, इसके बाद पूरे इनपुट का उपयोग करना है।
योना

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

0

यदि आप आदेश की परवाह नहीं करते हैं। फिर यह काम करेगा cat lines | tee >(tail -1)। जैसा कि दूसरों ने कहा है। आपको फ़ाइल को दो बार पढ़ने की ज़रूरत है, या पूरी फ़ाइल को बफर करने के लिए, आपके द्वारा पूछे गए क्रम में करने के लिए।

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