कैसे कम से कम वर्णों के साथ लाइन खोजने के लिए


22

मैं किसी भी सामान्य UNIX कमांड का उपयोग करके एक शेल स्क्रिप्ट लिख रहा हूं। मुझे उस लाइन को फिर से प्राप्त करना है जिसमें कम से कम अक्षर (व्हाट्सएप शामिल है)। लगभग 20 लाइनों तक हो सकता है।

मुझे पता है कि मैं head -$L | tail -1 | wc -mलाइन एल की वर्ण गणना को खोजने के लिए उपयोग कर सकता हूं । समस्या यह है कि, केवल एक ही तरीका है जिसके बारे में मैं सोच सकता हूं, वह यह है कि मानों की तुलना करते हुए, यदि कथनों में गड़बड़ी हो तो मैन्युअल रूप से लिखना होगा।

उदाहरण डेटा:

seven/7
4for
8 eight?
five!

4forकम से कम वर्ण होने के बाद वापसी करेंगे ।

मेरे मामले में, यदि कई लाइनों में सबसे कम लंबाई है, तो एक को वापस किया जाना चाहिए। इससे कोई फर्क नहीं पड़ता कि कौन सा चुना गया है, जब तक कि यह न्यूनतम लंबाई का हो। लेकिन मैं अन्य स्थितियों के साथ अन्य उपयोगकर्ताओं के लिए दोनों तरीके दिखाने में नुकसान नहीं देखता।


5
यदि 4 की लंबाई के साथ कई रेखाएं हों तो क्या होगा? क्या उन्हें भी छापा जाना चाहिए?
अराजक

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

जवाबों:


13

एक पर्ल तरीका है। ध्यान दें कि यदि समान, सबसे छोटी लंबाई की कई लाइनें हैं, तो यह दृष्टिकोण केवल उनमें से एक को प्रिंट करेगा:

perl -lne '$m//=$_; $m=$_ if length()<length($m); END{print $m if $.}' file 

व्याख्या

  • perl -lne: का -nअर्थ है "इनपुट फ़ाइल लाइन को लाइन द्वारा पढ़ें", -lप्रत्येक इनपुट लाइन से हटाए जाने वाली नई सूचियों का कारण बनता है और प्रत्येक printकॉल में एक नई लाइन जोड़ी जाती है ; और -eवह स्क्रिप्ट है जिसे प्रत्येक पंक्ति पर लागू किया जाएगा।
  • $m//=$_: $mवर्तमान लाइन पर सेट ( $_) जब तक $mपरिभाषित नहीं किया जाता है। //=ऑपरेटर पर्ल 5.10.0 के बाद से उपलब्ध है।
  • $m=$_ if length()<length($m): यदि वर्तमान मान की $mलंबाई वर्तमान रेखा की लंबाई से अधिक है, तो वर्तमान रेखा को ( $_) के रूप में सहेजें $m
  • END{print $m if $.}: एक बार सभी लाइनें संसाधित हो जाने के बाद $m, सबसे छोटी रेखा का वर्तमान मान प्रिंट करें । यह if $.सुनिश्चित करता है कि यह केवल तब होता है जब लाइन नंबर ( $.) को परिभाषित किया जाता है, रिक्त इनपुट के लिए एक खाली लाइन को प्रिंट करने से बचता है।

वैकल्पिक रूप से, चूंकि आपकी फ़ाइल मेमोरी में फिट होने के लिए काफी छोटी है, आप यह कर सकते हैं:

perl -e '@K=sort{length($a) <=> length($b)}<>; print "$K[0]"' file 

व्याख्या

  • @K=sort{length($a) <=> length($b)}<>: <>यहाँ एक सरणी है, जिसके तत्व फ़ाइल की पंक्तियाँ हैं। sortउनकी लंबाई के अनुसार क्रमित जाएगा और अनुसार क्रमबद्ध लाइनों सरणी के रूप में सहेजे जाते हैं @K
  • print "$K[0]": सरणी का पहला तत्व प्रिंट करें @K: सबसे छोटी पंक्ति।

यदि आप सभी छोटी लाइनों को प्रिंट करना चाहते हैं, तो आप उपयोग कर सकते हैं

perl -e '@K=sort{length($a) <=> length($b)}<>; 
         print grep {length($_)==length($K[0])}@K; ' file 

1
-Cबाइट्स की संख्या के बजाय वर्णों की संख्या के संदर्भ में लंबाई को मापने के लिए जोड़ें । UTF-8 लोकेल में, (2 बनाम 3) की $$तुलना में कम बाइट्स हैं , लेकिन अधिक वर्ण (2 बनाम 1)।
स्टीफन चेजलस

17

के साथ sqlite3:

sqlite3 <<EOT
CREATE TABLE file(line);
.import "data.txt" file
SELECT line FROM file ORDER BY length(line) LIMIT 1;
EOT

यहाँ एक मेरा पसंदीदा है, SQL के बारे में कभी नहीं सोचा ...
अराजकता

2
यह कोड गोल्फ स्टेटस होशियार है
शैडल्कर

2
क्या यह पूरी फ़ाइल को मेमोरी में पढ़ेगा और / या दूसरी ऑन-डिस्क कॉपी बनाएगा? यदि हां, तो यह चतुर है लेकिन अक्षम है।
जॉन कुगेलमैन

1
@JohnKugelman यह शायद पूरे 4 लाइनों को एक अस्थायी मेमोरी केवल डेटाबेस में सोख लेगा (जो कि straceइंगित करता है)। यदि आपको वास्तव में बड़ी फ़ाइलों के साथ काम करने की आवश्यकता है (और आपका सिस्टम स्वैप नहीं कर रहा है), तो आप इसे केवल एक फ़ाइल नाम जैसे जोड़कर बाध्य कर सकते हैं sqlite3 $(mktemp)और सभी डेटा डिस्क पर लिखे जाएंगे।
फ्लोइम स्वयं

मुझे निम्नलिखित त्रुटियां मिलती हैं: "" "xaa: 8146: unescaped" वर्ण "" "और" "" xaa: 8825: अपेक्षित 1 कॉलम, लेकिन पाया गया 2 - अतिरिक्त "" अनदेखा किया गया है। फ़ाइल में प्रत्येक पंक्ति में 1 प्रति json 1 होते हैं। ।
अहमदोव

17

यहाँ एक संस्करण है awk पहले पाया न्यूनतम लाइन मुद्रण के लिए समाधान :

awk '
  NR==1 || length<len {len=length; line=$0}
  END {print line}
'

जिसे सभी न्यूनतम लाइनों को मुद्रित करने के लिए बस एक शर्त द्वारा बढ़ाया जा सकता है:

awk '
  length==len {line=line ORS $0}
  NR==1 || length<len {len=length; line=$0}
  END {print line}'
'

12

पायथन काफी संक्षिप्त रूप से सामने आता है, और यह कोड टिन पर क्या कहता है:

python -c "import sys; print min(sys.stdin, key=len),"

अंतिम अल्पविराम अस्पष्ट है, मैं मानता हूं। यह अतिरिक्त लाइनब्रेक जोड़कर प्रिंट स्टेटमेंट को रोकता है। इसके अतिरिक्त, आप इसे Python 3 में लिख सकते हैं, जो 0 लाइनों का समर्थन करता है:

python3 -c "import sys; print(min(sys.stdin, key=len, default='').strip('\n'))"


टिन क्या कहता है?
मिकसेर

@mikeserve: यह कहता है, "sys.stdin का न्यूनतम प्रिंट करता है, लेन का उपयोग कुंजी के रूप में करता है; ;-)
स्टीव जेसप

1
आह। बाइनरी आकार, निर्भरता रेंगना या निष्पादन समय के बारे में कुछ भी नहीं है?
मिकसेर

2
@ बाइक: नहीं, छोटे प्रिंट टिन पर नहीं है। यह एक बंद फाइलिंग कैबिनेट में एक सलाहकार पत्रक पर है, एक तहखाने में, "तेंदुए से सावधान" एक दरवाजे के पीछे।
स्टीव जेसप

पकड़ लिया - तो प्रदर्शन पर।
मिकसेर

10

मैं हमेशा शुद्ध शेल स्क्रिप्टिंग (कोई निष्पादन नहीं!) के साथ समाधान पसंद करता हूं।

#!/bin/bash
min=
is_empty_input="yes"

while IFS= read -r a; do
    if [ -z "$min" -a "$is_empty_input" = "yes" ] || [ "${#a}" -lt "${#min}" ]; then
        min="$a"
    fi
    is_empty_input="no"
done

if [ -n "$a" ]; then
    if [ "$is_empty_input" = "yes" ]; then
        min="$a"
        is_empty_input="no"
    else
        [ "${#a}" -lt "${#min}" ] && min="$a"
    fi
fi

[ "$is_empty_input" = "no" ] && printf '%s\n' "$min"

ध्यान दें :

इनपुट में NUL बाइट्स के साथ एक समस्या है। इसलिए, इसके बजाय printf "ab\0\0\ncd\n" | bash this_scriptप्रिंट ।abcd


यह वास्तव में सबसे शुद्ध है। हालाँकि, परीक्षणों की अनाड़ीता bashमुझे sortइसके बजाय एक मध्यवर्ती परिणाम को पाइप करने के लिए मनाएगी ।
ओरियन

2
क्या आपने अपना नो एक्जीक्यूट करने की कोशिश की है ! समाधान बनाम अन्य जो करते हैं? यहाँ निष्पादन के बीच प्रदर्शन के अंतर की तुलना है ! और कोई निष्पादन नहीं! इसी तरह की समस्या के लिए समाधान। एक अलग प्रक्रिया को क्रियान्वित करना बहुत ही कम लाभप्रद होता है जब मकड़ियों - जैसे रूपों में var=$(get data)क्योंकि यह डेटा प्रवाह को एक ही संदर्भ में प्रतिबंधित करता है - लेकिन जब आप पाइप लाइन के माध्यम से डेटा को स्थानांतरित करते हैं - एक धारा में - प्रत्येक लागू निष्पादन आमतौर पर सहायक होता है - क्योंकि यह विशेष सक्षम बनाता है केवल आवश्यक जहां मॉड्यूलर कार्यक्रमों के आवेदन।
चाटुकार

1
@DigitalTrauma - अंकों का एक विस्तारित सन्निहित स्ट्रिंग, किसी भी अन्य विस्तारित स्ट्रिंग की तुलना में आवश्यक खोल से मुक्त शर्तों से अधिक या कम छूट नहीं है। $IFSडिजिट-भेदभावपूर्ण नहीं है - भले ही कोई डिफ़ॉल्ट $IFSमान नहीं है, हालांकि कई गोले एक पूर्व निर्धारित पर्यावरण कॉन्फ़िगरेशन को स्वीकार करेंगे $IFS- और इसलिए यह विशेष रूप से विश्वसनीय डिफ़ॉल्ट नहीं है।
चाटुकार


1
सभी टिप्पणियों और upvotes के लिए धन्यवाद (कुछ प्रतिनिधि को मेरे उत्तर को सही करने के लिए @cuonglm पर जाना चाहिए)। आम तौर पर मैं दूसरों को दैनिक रूप से शुद्ध शेल स्क्रिप्टिंग का अभ्यास करने की सलाह नहीं देता लेकिन उस कौशल को कुछ चरम स्थितियों में बहुत उपयोगी पाया जा सकता है जहां स्थिर लिंक के अलावा कुछ भी /bin/shउपलब्ध नहीं है। यह मेरे साथ कई बार हुआ है SunOS4 मेजबान के साथ /usrहार या कुछ .soक्षतिग्रस्त हो गया है, और अब आधुनिक लिनक्स युग में मैं अभी भी कभी-कभी एम्बेडेड सिस्टम या बूट फेलिंग सिस्टम के साथ समान स्थितियों का सामना करता हूं। बिजीबॉक्स उन महान चीजों में से एक है जिसे हमने हाल ही में हासिल किया है।
ययागशी

9

यहां एक शुद्ध zshसमाधान (यह सभी लाइनों को न्यूनतम लंबाई के साथ प्रिंट करता है file):

IFS=$'\n'; print -l ${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}

उदाहरण इनपुट:

seven/7
4for
8 eight?
five!
four

आउटपुट है:

4for
four

मुझे लगता है कि इसे एक छोटी व्याख्या की आवश्यकता है :-)


सबसे पहले, हम आंतरिक क्षेत्र विभाजक को newline पर सेट करते हैं:

IFS=$'\n';

अब तक इतना अच्छा, अब कठिन हिस्सा। रिक्तियों के स्थान पर परिणाम को नए समाचारों द्वारा अलग करने के printलिए -lध्वज का उपयोग करता है ।

अब, हम अंदर से शुरू करते हैं:

$(<file)

फ़ाइल को लाइन द्वारा लाइन में पढ़ा जाता है और सरणी के रूप में माना जाता है। फिर:

${(o@)...//?/?}

oध्वज का कहना है कि परिणाम, आरोही क्रम में आदेश दिया जाना चाहिए @भी सरणी के रूप में परिणाम के इलाज के लिए साधन। पीछे का हिस्सा ( //?/?) एक प्रतिस्थापन है जो सभी वर्णों को एक के साथ बदल देता है ?। अभी व:

${~...[1]}

हम पहले सरणी तत्व को लेते हैं [1], जो आपके मामले में सबसे छोटा है ????

${(M)$(<file):#...}

मिलान प्रत्येक सरणी तत्वों पर अलग-अलग किया जाता है, और बेजोड़ सरणी तत्व हटा दिए जाते हैं ( M)। प्रत्येक तत्व जो मेल खाता है ????(4 वर्ण) सरणी में रहता है। तो शेष तत्व वे हैं जिनके 4 वर्ण हैं (सबसे छोटा)।

संपादित करें: यदि आपको सबसे छोटी लाइनों में से केवल एक की आवश्यकता है, तो यह संशोधित संस्करण पहले वाले को प्रिंट करता है:

IFS=$'\n'; print -l ${${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}[1]}

8
tr -c \\n 1 <testfile |   #first transform every [^\n] char to a 1
grep -nF ''           |   #next get line numbers
paste -d: - testfile  |   #then paste it together with itself
sort  -t: -nk2,2          #then sort on second field

... और विजेता है ... पंक्ति 2, ऐसा प्रतीत होगा।

2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?

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

tr -c \\n \  <testfile    |   #transform all [^\n] to <space>
unexpand -t10             |   #squeeze every series of 10 to one tab
grep -nF ''               |   #and get the line numbers
sed    's/:/!d;=;:/;h;:big    #sed compares sequential lines
$P;$!N; /\(:[^ ]*\)\( *\)\n.*\1.*\2/!D     #newest line is shorter or...
        g;/:./!q;b big'   |   #not; quit input entirely for blank line
sed -f - -e q testfile        #print only first occurrence of shortest line

वह प्रिंट करता है ...

2
4for

एक और, बस sed:

sed -n '/^\n/D;s/\(.\)\(\n.*\)*/\1/g
$p;h;   s// /g;G;x;n;//!g;H;s// /g
G;      s/^\( *\)\(\n \1 *\)\{0,1\}\n//
D'      <infile >outfile

वाक्यविन्यास मानकों का अनुपालन है - लेकिन यह कोई गारंटी नहीं है कि कोई भी पुराना सही ढंग से sedसंभाल लेगा \(reference-group\)\{counts\}- कई नहीं।

यह मूल रूप से एक ही regexp को बार-बार इनपुट पर लागू करता है - जो कि उन्हें संकलित करने का समय होने पर बहुत फायदेमंद हो सकता है। वह पैटर्न है:

\(.\)\(\n.*\)*

जो अलग-अलग तरीके से अलग-अलग तार से मेल खाता है। उदाहरण के लिए:

string1\nstring2\nstring3

... sमें \1और ''शून्य स्ट्रिंग के साथ मिलान किया गया है \2

1\nstring2\nstring3

... 1में \1और के साथ मेल खाता \nstring2\nstring3है\2

\nstring2\nstring3

... \nमें \1और ''शून्य स्ट्रिंग के साथ मिलान किया गया है \2। यह समस्याग्रस्त होगा यदि \nपैटर्न स्पेस के सिर पर होने वाली ईवलाइन का कोई भी मौका था - लेकिन इसे रोकने के लिए कमांड /^\n/Dऔर //!gकमांड का उपयोग किया जाता है। मैंने उपयोग किया, [^\n]लेकिन इस छोटी सी स्क्रिप्ट के लिए अन्य जरूरतों ने पोर्टेबिलिटी को एक चिंता का विषय बना दिया और मैं कई मायनों में संतुष्ट नहीं था, जिसे अक्सर गलत समझा जाता है। साथ ही, .तेज है।

\nstring2
string1

... मैच \nऔर sफिर में \1और दोनों में ''शून्य स्ट्रिंग मिलता है \2। खाली लाइनें बिल्कुल मेल नहीं खाती हैं।

जब पैटर्न को दो gपक्षपाती रूप से लागू किया जाता है - दोनों बाएं-सबसे मानक पूर्वाग्रह और कम दाएं-साइड \nईलाइन बायस - एक स्किप को प्रभावित करने के लिए काउंटर-संतुलित होते हैं। कुछ उदाहरण:

s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g

... यदि सभी निम्नलिखित स्ट्रिंग के लिए (उत्तराधिकार में नहीं) लागू होते हैं ...

string1\nstring2

... इसे बदल देंगे ...

s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1: 
 : : : : : : :\nstring2

मूल रूप से मैं regexp का उपयोग हमेशा किसी भी पैटर्न-स्पेस में केवल पहली पंक्ति को संभालने के लिए करता हूं जिससे मैं इसे लागू करता हूं। यह मुझे दोनों बनाए रखने वाले शॉर्ट-मैच-अब तक लाइन के दो अलग-अलग संस्करणों को हथकंडा करने में सक्षम बनाता है और सबसे हाल ही में लाइन का उपयोग करने के लिए परीक्षण छोरों का सहारा लिए बिना - हर प्रतिस्थापन लागू एक ही बार में पूरे पैटर्न-स्थान को संभालता है।

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

यह दुर्भाग्यपूर्ण है कि एक और आवश्यकता एक ही संभालने के लिए बहुत सारे बफर स्विचिंग है - लेकिन कम से कम न तो बफर वर्तमान में रहने के लिए आवश्यक चार लाइनों से अधिक है - और इसलिए शायद यह भयानक नहीं है।

वैसे भी, प्रत्येक चक्र के लिए पहली चीज जो याद की गई रेखा पर परिवर्तन है - क्योंकि वास्तव में बचाई गई एकमात्र प्रति शाब्दिक है - ...

^               \nremembered line$

... और बाद में nएक्सट्रीम इनपुट लाइन किसी भी पुराने बफर को ओवरराइट कर देती है। यदि इसमें कम से कम एक वर्ण नहीं है तो इसे प्रभावी रूप से अनदेखा कर दिया जाता है। यह qपहली बार होने वाली ब्लैंक लाइन पर यूट करने के लिए बहुत आसान होगा , लेकिन, ठीक है, मेरे टेस्ट डेटा में बहुत सारे थे और मैं कई पैराग्राफ को संभालना चाहता था।

और इसलिए यदि इसमें एक वर्ण समाहित है तो इसका शाब्दिक संस्करण याद की गई रेखा से जुड़ा है और इसका स्पेसिफाइड तुलना संस्करण इस तरह से पैटर्न स्पेस के प्रमुख पर स्थित है:

^   \n               \nremembered line\nnew$

अंतिम प्रतिस्थापन उस पैटर्न स्पेस पर लागू होता है:

s/^\( *\)\(\n \1 *\)\{0,1\}\n//

इसलिए यदि नई लाइन में याद रखने वाली लाइन को फिट करने के लिए आवश्यक जगह हो सकती है, तो कम से कम एक चार को छोड़े जाने के लिए पहले दो पंक्तियों को प्रतिस्थापित किया जाता है, केवल पहले को।

परिणाम की परवाह किए बिना पैटर्न स्पेस में पहली पंक्ति को Dफिर से शुरू करने से पहले हमेशा एंड-ऑफ-साइकिल पर eleted किया जाता है। इसका मतलब है कि अगर नई लाइन पिछले स्ट्रिंग से छोटी है ...

new

... चक्र में पहले प्रतिस्थापन के लिए वापस भेजा जाता है जो हमेशा केवल पहली नईलाइन चार पर से पट्टी करेगा - और इसलिए यह संपूर्ण रहता है। लेकिन अगर यह नहीं है तो स्ट्रिंग ...

remembered line\nnew

... इसके बजाय अगला चक्र शुरू होगा, और पहले प्रतिस्थापन स्ट्रिंग से यह पट्टी करेगा ...

\nnew

...हर बार।

अंतिम पंक्ति में याद की गई पंक्ति को मानक के रूप में प्रिंट किया जाता है, और इसलिए दिए गए उदाहरण के डेटा के लिए, यह प्रिंट करता है:

4for

लेकिन, गंभीरता से, उपयोग करें tr



क्या आपको लाइन नंबर डालने की भी आवश्यकता है? ओपी की मेरी रीडिंग यह है कि बस सबसे छोटी लाइन की आवश्यकता है, और जरूरी नहीं कि उस लाइन की लाइन संख्या। मुझे लगता है कि इसे पूर्णता के लिए दिखाने में कोई बुराई नहीं है।
डिजिटल ट्रॉमा

@DigitalTrauma - नहीं, शायद नहीं। लेकिन यह उनके बिना शायद ही बहुत उपयोगी है - और वे इतने सस्ते में आते हैं। जब मैं एक धारा का काम करता हूं तो मैं हमेशा आउटपुट में मूल इनपुट को पुन: प्रस्तुत करने का एक साधन शामिल करना पसंद करता हूं - लाइन-नंबर यहां संभव बनाते हैं। उदाहरण के लिए, पहले पाइपलाइन के परिणामों को चारों ओर मोड़ने के लिए REINPUT | sort -t: -nk1,1 | cut -d: -f3-:। और दूसरा sed --expressionपूंछ में एक और स्क्रिप्ट को शामिल करने का एक सरल मामला है ।
चाटुकार

@DigitalTrauma - ओह, और पहले उदाहरण में लाइन नंबर एक टाई-ब्रेकर के रूप में व्यवहार को प्रभावित करते हैं sortजब इनपुट में समान-लंबाई की रेखाएं होती हैं - तो सबसे जल्दी होने वाली रेखा हमेशा उस मामले में शीर्ष पर तैरती है।
माइकस

7

प्रयत्न:

awk '{ print length, $0 }' testfile | sort -n | cut -d" " -f2- | head -1

विचार awkप्रत्येक पंक्ति की लंबाई को पहले प्रिंट करने के लिए उपयोग करना है। यह इस प्रकार दिखाई देगा:

echo "This is a line of text" | awk '{print length, $0}'
22 This is a line of text

फिर, गिनती से छुटकारा पाने के लिए और पहली पंक्ति (कम से कम अक्षरों वाले एक) को रखने के लिए sort, लाइनों को छांटने के लिए वर्ण गणना का उपयोग करें । आप निश्चित रूप से इस मामले में सबसे अधिक पात्रों के साथ लाइन प्राप्त करने के लिए उपयोग कर सकते हैं ।cutheadtail

(यह इस उत्तर से अपनाया गया था )


+1 तर्क के लिए लेकिन यह सभी मामलों में काम नहीं करेगा। यदि दो पंक्तियों में समान वर्ण हैं और जो न्यूनतम है। यह आपको केवल पहली पंक्ति देगा जो कि सामना करना पड़ रहा हैhead -1
तुषी

सबसे लंबी लाइन प्राप्त करने के लिए, यह प्रयोग करने की तुलना में सॉर्ट को उल्टा करने के लिए थोड़ा अधिक कुशल है tail( headशेष इनपुट पढ़े बिना, जैसे ही इसका काम पूरा हो सकता है, बाहर निकल सकता है)।
टोबे स्पाइट

@ थुशी रेगेक्स के एक बिट का उपयोग करते हुए, लाइन नंबरों को प्रिंट करने के बाद, सब कुछ लेकिन लाइन 1 के समान संख्या वाली लाइनों को हटाया जा सकता है, इस प्रकार सभी छोटी लाइनों को आउटपुट किया जाता है।
मैथ्यू डी। शोलेफील्ड

5

POSIX awk के साथ:

awk 'FNR==1{l=$0;next};length<length(l){l=$0};END{print l}' file

यह काम नहीं करेगा यदि एक से अधिक लाइन में समान वर्ण हों और जो न्यूनतम भी हो।
तुशी

@ तुशी: यह पहली न्यूनतम रेखा की सूचना देगा।
congonglm

हाँ। लेकिन यह सही आउटपुट सही नहीं है? यहां तक ​​कि अन्य पंक्तियों में न्यूनतम संख्या में वर्ण हैं।
तुषी

1
@ तुशी: कि ओपी की आवश्यकता का उल्लेख नहीं है, ओपी से अद्यतन की प्रतीक्षा कर रहा है।
congonglm

3
मुझे नहीं लगता Lकि वैरिएबल का नाम चुनने के लिए सबसे अच्छा पत्र था: D कुछ ऐसा minहोगा जो चीजों को और अधिक स्पष्ट करेगा
Fedorqui

3

@ Mikeserv के विचारों में से कुछ उधार लेना:

< testfile sed 'h;s/./:/g;s/.*/expr length "&"/e;G;s/\n/\t/' | \
sort -n | \
sed -n '1s/^[0-9]+*\t//p'

सबसे पहला sed निम्नलिखित करता है:

  • h होल्ड बफ़र के लिए मूल पंक्ति को सहेजता है
  • प्रत्येक वर्ण को पंक्ति में बदलें : - यह कोड इंजेक्शन के किसी भी खतरे को दूर करना है
  • पूरी लाइन के साथ बदलें expr length "whole line" - यह एक शेल अभिव्यक्ति है जिसका मूल्यांकन किया जा सकता है
  • ई कमांडs एक हैपैटर्न स्पेस का मूल्यांकन करने और परिणाम को पैटर्न स्पेस में वापस लाने के GNU सेड एक्सटेंशन है।
  • G पैटर्न स्पेस के लिए एक नई रेखा और होल्ड स्पेस (मूल लाइन) की सामग्री को जोड़ता है
  • अंतिम s टैब के साथ नई पंक्ति को प्रतिस्थापित करता है

वर्णों की संख्या अब प्रत्येक पंक्ति के प्रारंभ में एक संख्या है, इसलिए sort -n पंक्ति की लंबाई के अनुसार क्रमबद्ध करें।

फ़ाइनल sedफिर सभी को निकालता है लेकिन पहली (सबसे छोटी) लाइन और लाइन की लंबाई और परिणाम को प्रिंट करता है।


1
@ माइकर्स हाँ मुझे लगता exprहै कि यहाँ अच्छा है। हां, eप्रत्येक पंक्ति के लिए एक शेल स्पॉन करेगा। मैंने sed अभिव्यक्ति को संपादित किया ताकि यह स्ट्रिंग से प्रत्येक चार :को बदले के पहले स्ट्रिंग में बदल दे जो मुझे लगता है कि कोड इंजेक्शन की किसी भी संभावना को दूर करना चाहिए।
डिजिटल ट्रॉमा

मैं आमतौर पर xargs exprव्यक्तिगत रूप से विकल्प चुनूंगा - लेकिन, मध्यवर्ती शेल से बचने के अलावा, यह शायद एक शैलीगत बात है। मुझे यह पसंद है, वैसे भी।
चाटुकार

3

यह मेरे लिए हुआ कि एक sedअभिव्यक्ति में पूरी बात संभव है । यह बहुत सुंदर नहीं है:

$ sed '1h;s/.*/&\n&/;G;:l;s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/;tl;/\n\n/{s/\n.*//;x};${x;p};d' testfile
4for
$ 

इसे तोड़ना:

1h            # save line 1 in the hold buffer (shortest line so far)
s/.*/&\n&/    # duplicate the line with a newline in between
G             # append newline+hold buffer to current line
:l            # loop start
s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/
              # attempt to remove 1 char both from current line and shortest line
tl            # jump back to l if the above substitution succeeded
/\n\n/{       # matches if current line is shorter
  s/\n.*//    # remove all but original line
  x           # save new shortest line in hold buffer
}
${            # at last line
  x           # get shortest line from hold buffer
  p           # print it
}
d             # don't print any other lines

OS X में BSD का सेड कुछ ज्यादा ही नया है। यह संस्करण BSD और GNU दोनों सेड के संस्करणों के लिए काम करता है:

$ sed -e '1h;G;s/\([^\n]*\)\(\n\)\(.*\)/\1\2\1\2\3/;:l' -e 's/\(\n\)[^\n]\([^\n]*\n\)[^\n]/\1\2/;tl' -e '/\n\n/{s/\n.*//;x;};${x;p;};d' testfile
4for
$

ध्यान दें कि यह एक "क्योंकि इसका संभावित" उत्तर एक सर्वोत्तम अभ्यास उत्तर देने के गंभीर प्रयास से अधिक है। मुझे लगता है इसका मतलब है कि मैं बहुत ज्यादा कोड-कॉल खेल रहा हूं


man sedओएस एक्स पर @mikeserv : "एस्केप सीक्वेंस \ n पैटर्न स्पेस में एम्बेडेड एक नई लाइन वर्ण से मेल खाता है" । इसलिए मुझे लगता है कि GNU sed \nरिजेक्स में और प्रतिस्थापन \nमें अनुमति देता है, जबकि BSD केवल रेगेक्स में अनुमति देता है न कि प्रतिस्थापन में।
डिजिटल ट्रॉमा

\nपैटर्न स्पेस से उधार लेना एक अच्छा विचार है और दूसरी s///अभिव्यक्ति में काम करेगा , लेकिन s/.*/&\n&/एक्सप्रेशन \nको उस स्पेस स्पेस में डाला जा रहा है, जहां पहले कोई नहीं था। इसके अलावा BSD sed लेबल परिभाषाओं और शाखाओं के बाद शाब्दिक नई सुर्खियों की आवश्यकता प्रतीत होती है।
डिजिटल ट्रामा

1
वे न्यूलाइन्स पैरामीटर डेलिमिटर हैं - आपको उन्हें किसी भी कमांड को सीमांकित करने की आवश्यकता है जो एक मनमाना पैरामीटर स्वीकार कर सकता है - कम से कम, यही वह है जो कल्पना कहता है। युक्ति यह भी कहती है कि एक sedस्क्रिप्ट एक पाठ फ़ाइल होगी, सिवाय इसके कि उसे एक नई पंक्ति में समाप्त होने की आवश्यकता नहीं है । तो आप आमतौर पर उन्हें अलग-अलग आर्ग के रूप में अच्छी तरह से परिसीमन कर सकते हैं - sed -e :\ label -e :\ label2और इसी तरह। चूंकि आप 1hवैसे भी कर रहे हैं , आप x;Hअपनी नई लाइन पाने के लिए बस कुछ तर्क पर स्विच कर सकते हैं - और आप एक नई लाइन w / में खींचे बिना साइकिल के अंत में पैटर्न स्पेस से एक प्रमुख नई लाइन ट्रिम कर सकते हैं D
15 अक्टूबर को सुबह

@ माइकस नाइस। हां, मैंने Gपहली बार करने और s///अभिव्यक्ति को बदलने के लिए आवश्यक नई पंक्ति सम्मिलित की । इसका उपयोग करके इसे विभाजित करना -eसभी को एक (लंबी) रेखा पर जाने देता है जिसमें कोई शाब्दिक नई रूपरेखा नहीं है।
डिजिटल ट्रामा

\nबचने के लिए spec'd है sed, भी के एलएचएस, और मुझे लगता है कि कि शब्दशः कल्पना का बयान है, सिवाय इसके POSIX ब्रैकेट भाव भी इस तरह से कि सभी पात्रों को उनके विशेष अर्थ खो में spec'd कर रहे हैं कि - (स्पष्ट सहित \\) - कोष्ठक को छोड़कर, एक सीमा विभाजक के रूप में डैश, और टकराव, समतुल्यता, निषेध और वर्गों के लिए डॉट, बराबर, कैरेट, बृहदान्त्र।
15 अक्टूबर को सुबह

2

एक और पर्ल समाधान: लाइनों को हैश-ऑफ-सरणियों में स्टोर करें, हैश की लाइन की लंबाई हो। फिर, न्यूनतम कुंजी के साथ लाइनों का प्रिंट आउट लें।

perl -MList::Util=min -ne '
    push @{$lines{ length() }}, $_;
} END {
    print @{$lines{ min keys %lines }};
' sample 
4for

आप उपयोग कर सकते हैं push @{$lines{+length}};और print @{$lines{+min keys %lines}};कम टाइपिंग के लिए :)
cuonglm

अगर मैं गोल्फ खेल रहा होता, तो मैं "लाइन्स" नाम के चर का इस्तेमाल नहीं करता:perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
ग्लेन जैकमैन

एक गैर-गोल्फ संस्करण के लिए +1 (जो काम करता है!), हालांकि केवल सभी प्रकार के प्रिंट के लिए । - perlहममें से उन लोगों के लिए थोड़ा सा आनंद मिलता है, जो perlक्रिप्टिक प्रकृति के बराबर नहीं हैं। Btw। golfed के अंत में sayएक नकली खाली लाइन प्रिंट करता है। आउटपुट।
पीटर।

2

सिर्फ पहली सबसे छोटी रेखा पाने के लिए:

f=file; sed -n "/^$(sed 's/./1/g' $f | sort -ns | sed 's/././g;q')$/{p;q}" $f

सभी कम से कम लिनेट्स प्राप्त करने के लिए, बस में बदलना {p;q}होगाp


एक अन्य विधि (कुछ हद तक असामान्य) लंबाई केsort अनुसार वास्तविक छंटाई करना है । यह छोटी रेखाओं के साथ अपेक्षाकृत धीमी है, और रेखा की लंबाई बढ़ने के साथ नाटकीय रूप से धीमी हो जाती है।
हालांकि, मुझे ओवरलैपिंग कीज़ द्वारा सॉर्ट करने का विचार है काफी रोचक लगता है। मैं इसे पोस्ट कर रहा हूँ अगर दूसरों को भी यह दिलचस्प / जानकारीपूर्ण लगे।

यह कैसे काम करता है:
एक ही कुंजी के लंबाई-प्रकार के आधार पर क्रमबद्ध करें - key 1जो पूरी लाइन को फैलाता है
प्रत्येक क्रमिक कुंजी भिन्न एक वर्ण द्वारा कुंजी की लंबाई बढ़ाती है, फ़ाइल की सबसे लंबी रेखा (निर्धारित द्वारा wc -L) की लंबाई तक

केवल पहली (हल की गई) सबसे छोटी रेखा पाने के लिए:

f=file; sort -t'\0' $(seq -f "-k1.%0.0f" $(<"$f" wc -L) -1 1) "$f" | head -n1

जो समान है:

f=file.in; 
l=$(<"$f" wc -L)
k=$(seq -f "-k1.%0.0f" $l -1 1) 
sort -st'\0' $k "$f" | head -n1

2

मान लें कि रिक्त लाइनों को सबसे छोटी रेखा नहीं माना जाता है और वह खाली लाइनें मौजूद हो सकती हैं, निम्नलिखित शुद्ध AWK काम करेगा:

awk '
    {
        len   = length;
        a[$0] = len
    }
    !len { next }
    !min { min = len }
    len < min { min = len }
    END {
        for (i in a)
            if (min == a[i])
                print i
    }
' infile.txt

2

सॉर्ट का उपयोग करने के बारे में क्या?

awk '{ print length($0) "\t" $0 }' input.txt | sort -n | head -n 1 | cut -f2-

1

जीएनयू जाग के साथ

gawk '
    {
         a[length]=$0
    };
    END
    {
        PROCINFO["sorted_in"]="@ind_num_asc";
        for (i in a)
        {
            print a[i]; 
            exit
        }
    }
    ' file
  • प्रत्येक पंक्ति को पंक्ति लंबाई द्वारा अनुक्रमित सरणी में पढ़ें।

  • सरणी सूचकांक द्वारा क्रमबद्ध किए जाने वाले सरणी स्कैनिंग को बल के PROCINFO["sorted_in"]लिए सेट करें , संख्यात्मक रूप से क्रमबद्ध@ind_num_asc

  • PROCINFOऊपर दिए गए तरीके से सेटिंग सबसे छोटी लंबाई वाली रेखा को सरणी के ट्रैवर्सल में पहले उठाया जाता है। इसलिए एरे और एग्जिट से पहले एलिमेंट को प्रिंट करें

इससे nlognकुछ अन्य दृष्टिकोणों के nसमय में होने का नुकसान होता है


1

मध्य-स्तरीय शेल उपकरण विधि, नहीं sedया awk:

f=inputfile
head -n $(xargs -d '\n' -L 1 -I % sh -c 'exec echo "%" | wc -c' < $f | 
          cat -n | sort -n -k 2 | head -1 | cut -f 1)  $f | tail -1

यह एक $fचर की जरूरत नहीं करने के लिए अच्छा होगा ; मैं एक धारणा है कि teeकिसी भी तरह का उपयोग संभव हो सकता है ...
agc
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.