बैश में, मैं एक चर में लाइनों की संख्या कैसे गिनूं?


82

मेरे पास एक चर है जिसमें एक स्ट्रिंग संग्रहीत है और यह जांचने की आवश्यकता है कि क्या इसमें लाइनें हैं:

var=`ls "$sdir" | grep "$input"`

छद्म कोड:

while [ ! $var's number of lines -eq 1 ]
  do something

इसे कैसे जांचना है, इस पर मेरा विचार है। echo $var | wc -lकाम नहीं करता है - यह हमेशा कहता है 1, भले ही इसके पास है 3

echo -e साथ ही काम नहीं करता है।

जवाबों:


108

बात को कोट करता है।

echo "$var" | wc -l

3
यह कमांड के आस-पास के उद्धरण नहीं हैं, यह कमांड प्रतिस्थापन के आसपास के उद्धरण हैं। उद्धरणों के जोड़े हस्तक्षेप नहीं करेंगे।
इग्नासियो वाज़केज़-अब्राम्स

38
यह एक सूक्ष्मता है। एक खाली स्ट्रिंग 1 पर वापस आ जाएगी क्योंकि खाली स्ट्रिंग पर प्रतिध्वनि एक नई रेखा प्रिंट करती है।
एंड्रयू गुयेन

3
दूसरे शब्दों में: if [ -z "$var" ]; then printf '%s\n' '0'; else printf '%s\n' "${var%$'\n'}" | wc -l; fivar=(कोई रेखाएँ), var='foo'और var=$'foo\n'(दोनों एक पंक्ति में * nix sense) आज़माएँ ।
l0b0

2
और फिर भी इसे चर में लाने का एक और तरीका: LINE_COUNT=$(wc -l <<< "${var}")
आकर्षक जू

4
@ टिम echo -nबस एक एक करके गिनती कम कर देगा। @lucifurious echo $(wc -l <<< "${NONEXISTENTVAR}")अभी भी 1 देता है, 0 नहीं
जूलियन

73

स्वीकृत उत्तर और अन्य उत्तर यहां पोस्ट किए गए रिक्त चर (अपरिभाषित या खाली स्ट्रिंग) के मामले में काम नहीं करते हैं।

यह काम:

echo -n "$VARIABLE" | grep -c '^'

उदाहरण के लिए:

ZERO=
ONE="just one line"
TWO="first
> second"

echo -n "$ZERO" | grep -c '^'
0
echo -n "$ONE" | grep -c '^'
1
echo -n "$TWO" | grep -c '^'
2

मैं पुन: पेश नहीं कर सकता कि @PolyTekPatrick से एक बग की तरह क्या लगता है, यानी WHITESPACE_ONE=' 'मेरे शेल (बैश) में अभी भी काम करता है, सही ढंग से एक लाइन रिपोर्टिंग।
जूलियन

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

4
उत्तम! यह स्वीकृत उत्तर होना चाहिए। यह अब तक का एकमात्र समाधान है कि सभी मामलों के लिए ठीक से प्रश्न का उत्तर दिया जाए। इसे साबित करने के लिए परीक्षण के मामलों को दिखाने के लिए धन्यवाद।
पॉलीटेकपैरिक

4
हाँ! मुझे भी लगता है कि यह स्वीकृत उत्तर होना चाहिए।
मार्टिन जॉइनर

1
यह सही है, लेकिन printfइसके बजाय का उपयोग करके सरल किया जा सकता है echo -n। मैंने परीक्षा परिणामों को शामिल करने के लिए इसे वैकल्पिक उत्तर के रूप में जोड़ा है। निचे देखो।
स्टिलेज

23

एक और तरीका है जो यहाँ के तारों को काटता है:

wc -l <<< "$var"

जैसा कि इस टिप्पणी में उल्लेख किया गया है , एक खाली $varका परिणाम 0 लाइनों के बजाय 1 पंक्ति में होगा क्योंकि यहां तार इस मामले में एक नया चरित्र जोड़ते हैं ( स्पष्टीकरण )।


1
मुझे सही उत्तर पाने के लिए ऐसा करना पड़ा: wc -l <<<"$(echo "$var")"(हां, हर एक प्रतीक आवश्यक था)
निकोलई एस

@NicolaiS तब आपने कुछ गलत किया। की सामग्री क्या थी $var?
१०:१४ पर १५

1
@NicolaiS यह सही है क्योंकि आपके varशामिल एक पंक्ति : आप व्याख्या नहीं \nकुछ भी साथ। कई लाइनों को अपने में रखें varऔर यह काम करेगा, जैसे कि var="foo<ENTER>bar<ENTER>baz"<ENTER>
8

2
xxd <<< ''इस हेक्सडम्प बनाएँ 00000000: 0a। इसलिए, <<<(यहां स्ट्रिंग्स) किसी भी सामग्री में न्यूलाइन वर्ण जोड़ते हैं।
मिनीमैक्स

1
@MiniMax धन्यवाद, मैंने अपने इनपुट को मेरे उत्तर में जोड़ दिया। आप यहां इस व्यवहार के लिए स्पष्टीकरण पा सकते हैं ।
speakr

9

आप "wc -w" के साथ "wc -w" को लाइनों के बजाय शब्दों की संख्या गिनने के लिए स्थानापन्न कर सकते हैं। यह किसी भी नई लाइनों की गिनती नहीं करेगा और यह परीक्षण करने के लिए इस्तेमाल किया जा सकता है कि क्या आपके मूल परिणाम आपके जारी रहने से पहले खाली हैं।


wc -lसमाधान 1 आउटपुट इनपुट के खाली होने पर भी, इसलिए wc -wउपयोग करने के लिए बेहतर होगा।
कादिर

9

किसी ने पैरामीटर विस्तार का उल्लेख नहीं किया है, इसलिए यहां शुद्ध बैश का उपयोग करने के कुछ तरीके हैं।

विधि 1

गैर-नई वर्णों को निकालें, फिर स्ट्रिंग की लंबाई प्राप्त करें + 1. उद्धरण महत्वपूर्ण हैं

 var="${var//[!$'\n']/}"
 echo $((${#var} + 1))

विधि 2

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

 set -f # disable glob (wildcard) expansion
 IFS=$'\n' # let's make sure we split on newline chars
 var=(${var})
 echo ${#var[@]}

2
विधि 2 क्लीनर है और शायद तेज है। Howver यह पर निर्भर करता है IFS। तो IFS=$'\n'यह सुनिश्चित करने के लिए सेट करें कि एक सरणी में विस्तार करते समय चर को नई लाइनों पर विभाजित किया गया है:IFS=$'\n'; var=(${var})
खोलें

मुझे इसकी न्यूनतम ओवरहेड की वजह से मेथड 2 पसंद है: कोई बाहरी आदेश और यहां तक ​​कि एक बिलिन भी दृष्टि में नहीं है। यह काफी पठनीय भी है।
DKroot

1
शेलचेक शिकायत करता है: github.com/koalaman/shellcheck/wiki/SC2206 । यह कुछ पर है। set -fअवांछित ग्लोब विस्तार से बचने के लिए आवश्यक है। विधि 2 का उपयोग करने का प्रयास करें var=$'*\n.*'
DKroot

शुद्ध आंतरिक बैश w / ओ के लिए स्वत: बोनस बाहरीwc -l
nhed

7

@ जूलियन के उत्तर का एक सरल संस्करण, जो सभी स्ट्रिंग्स के लिए काम करता है, उसके साथ या उसके बाद बिना n (यह एक फ़ाइल को गिनती करता है जिसमें खाली एकल \ n शामिल है):

printf "%s" "$a" | grep -c "^"

  • रिटर्न शून्य: अनसेट वेरिएबल, खाली स्ट्रिंग, स्ट्रिंग जिसमें नंगे न्यूलाइन होते हैं
  • रिटर्न 1: कोई भी नॉन-खाली लाइन, नई लाइन के साथ या उसके बिना
  • आदि

आउटपुट:

# a=
# printf "%s" "$a" | grep -c "^"
0

# a=""
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "\n")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf " \n")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf " ")"
# printf "%s" "$a" | grep -c "^"
1

# a="aaa"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n%s" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

# a="$(printf "%s\n%s\n" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

+1। echo(जैसे echo -n) झंडे को पास करना मानक नहीं है, और अलग-अलग कार्यान्वयन पर अलग-अलग परिणाम दे सकता है। जबकि printfडिफ़ॉल्ट रूप से हम जो चाहते हैं। एक बोनस के रूप में, का उपयोग printfकरके आप एक प्रक्रिया को बचाता है, क्योंकि यह एक शेल निर्मित है। (सुझाव: printf "$a" | wc -lअधिक संक्षिप्त है और अनावश्यक उपयोग से बचा जाता है grep)
joshtch

@joshtch खैर .. नहीं। जैसा कि इस चर्चा में अन्य लोगों ने कहा है कि printf .... | wc -lकेवल एक अतिरिक्त लाइन (न्यूलाइन) को हटा दिया जाएगा ताकि एक खाली लाइन के मामले में परिणाम 0 होगा । सही बात। लेकिन अगर हम 2 लाइनें पास करते हैं, तो परिणाम 1 होगा, जहां उसी चर printf ... | grep "^"को सही तरीके से पास किया जाएगा। 2. इसके अलावा, सीधे उपयोग printf "$a"करना बहुत खतरनाक है, क्योंकि यह गलत त्रुटियों को जन्म दे सकता है अगर स्ट्रिंग में गलती से वर्ण जैसे होते हैं %s, %dऔर इसी तरह पर ... वही अगर स्ट्रिंग एक पानी का छींटा के साथ शुरू होता है। इसके बजाय, 2 पैरामीटर में printfस्वचालित रूप से बच जाता है
ल्यूक सेवफ्रॉग्स

3

यदि कोई परिणाम जीआरपी द्वारा वापस नहीं किया गया तो शीर्ष मतदान के उत्तर विफल हो जाते हैं।

Homer Simpson
Marge Simpson
Bart Simpson
Lisa Simpson
Ned Flanders
Rod Flanders
Todd Flanders
Moe Szyslak

यह वह जगह है गलत यह करने के लिए जिस तरह से :

wiggums=$(grep -iF "Wiggum" characters.txt);
num_wiggums=$(echo "$wiggums" | wc -l);
echo "There are ${num_wiggums} here!";

हमें बताएंगे, सूची में 1 विगगम है, भले ही कोई भी क्यों न हो।

इसके बजाय, आपको यह देखने के लिए एक अतिरिक्त जांच करने की आवश्यकता है कि चर खाली है (या -z, जैसा कि "शून्य" है)। अगर grep ने कुछ भी वापस नहीं किया, तो चर खाली हो जाएगा।

matches=$(grep -iF "VanHouten" characters.txt);

if [ -z "$matches" ]; then
    num_matches=0;
else
    num_matches=$(echo "$matches" | wc -l);
fi

echo "There are ${num_matches} VanHoutens on the list";

2

एक चर में लाइनों की संख्या की गणना करने का एक और तरीका - मान लें कि आपने जांच की थी कि यह सफलतापूर्वक भरा गया है या यह खाली नहीं है, इसके लिए सिर्फ $ की जांच करें? var subshell परिणाम प्रभावित होने के बाद -:

readarray -t tab <<<"${var}"
echo ${#tab[@]}

readarray | Mapfile bash इंटरनल कमांड है, जो इनपुटलाइन , या यहाँ स्ट्रिंग को इस मामले में, न्यूलाइन्स के आधार पर सरणी में परिवर्तित करता है।

-t फ्लैग सरणी की कोशिकाओं के अंत में नए सिरे को संग्रहीत करने से रोकता है, बाद में संग्रहीत मूल्यों के उपयोग के लिए उपयोगी है

इस विधि के लाभ हैं:

  • कोई बाहरी आदेश (wc, grep, ...)
  • कोई उपधारा (पाइप) नहीं
  • कोई IFS मुद्दे (संशोधन के बाद बहाल, आंतरिक आदेशों पर कमांड-सीमित गुंजाइश के साथ उपयोग करने के लिए मुश्किल, ...)

1

"Wc -l" कमांड में फ़ाइल नाम से बचने के लिए:

lines=$(< "$filename" wc -l)
echo "$lines"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.