शेल कमांड के आउटपुट में वर्णों की संख्या


12

मैं एक स्क्रिप्ट लिख रहा हूं, जिसे एक चरण में कमांड के आउटपुट में वर्णों की संख्या की गणना करने की आवश्यकता है ।

उदाहरण के लिए, कमांड का उपयोग readlink -f /etc/fstabकरना चाहिए, 10क्योंकि उस कमांड का आउटपुट 10 वर्ण लंबा है।

यह निम्नलिखित कोड का उपयोग करके संग्रहीत चर के साथ पहले से ही संभव है:

variable="somestring";
echo ${#variable};
# 10

दुर्भाग्य से, कमांड-जनरेटेड स्ट्रिंग के साथ समान सूत्र का उपयोग करने से काम नहीं होता है:

${#(readlink -f /etc/fstab)};
# bash: ${#(readlink -f /etc/fstab)}: bad substitution

मैं समझता हूं कि आउटपुट को पहले वेरिएबल में सेव करके ऐसा करना संभव है:

variable=$(readlink -f /etc/fstab);
echo ${#variable};

लेकिन मैं अतिरिक्त कदम हटाना चाहूंगा।

क्या यह संभव है? केवल निर्मित या मानक उपयोगिताओं का उपयोग करके अल्मक्विस्ट शेल (श) के साथ संगतता बेहतर है।


1
के उत्पादन में readlink -f /etc/fstabहै 11 अक्षर। न्यूलाइन को न भूलें। अन्यथा आप देखेंगे /etc/fstabluser@cern:~$ कि आपने इसे शेल से कब चलाया था।
फिल फ्रॉस्ट

@PhilFrost आपको एक मज़ेदार संकेत लगता है, क्या आप CERN में काम करते हैं?
दिमित्री ग्रिगोरीव

जवाबों:


9

GNU एक्सप के साथ :

$ expr length + "$(readlink -f /etc/fstab)"
10

+वहाँ है जीएनयू की एक खास विशेषता यह exprयकीन है कि अगले तर्क स्ट्रिंग के रूप में व्यवहार किया जाता है, भले ही यह एक होता है बनाने के लिए exprकी तरह ऑपरेटर match, length, +...

उपरोक्त आउटपुट के किसी भी अनुगामी न्यूलाइन को छीन लेगा। इसके आसपास काम करने के लिए:

$ expr length + "$(readlink -f /etc/fstab; printf .)" - 2
10

परिणाम को 2 तक घटाया गया क्योंकि अंतिम readlinkवर्ण और .हमने जो चरित्र जोड़ा था।

यूनिकोड स्ट्रिंग के साथ, exprकाम नहीं लगता है, क्योंकि यह वर्ण गणना के बजाय बाइट्स में स्ट्रिंग की लंबाई लौटाता है ( पंक्ति 654 देखें )

$ LC_ALL=C.UTF-8 expr length ăaa
4

तो, आप उपयोग कर सकते हैं:

$ printf "ăaa" | LC_ALL=C.UTF-8 wc -m
3

POSIXLY:

$ expr " $(readlink -f /etc/fstab; printf .)" : ".*" - 3
10

कमांड प्रतिस्थापन से पहले का स्थान कमांड को स्ट्रिंग स्टार्ट के साथ क्रैश होने से रोकता है -, इसलिए हमें 3 को घटाना होगा।


धन्यवाद! ऐसा लगता है कि आपका तीसरा उदाहरण भी बिना काम करता है LC_ALL=C.UTF-8, जो चीजों को काफी सरल करता है अगर स्ट्रिंग के एन्कोडिंग को पहले से नहीं जाना जाएगा।
user339676

2
expr length $(echo "*")- नहीं। कम से कम दोहरे उद्धरण चिह्नों का उपयोग करें expr length "$(…)":। लेकिन यह कमांड से नए सिरे को पीछे छोड़ता है, यह कमांड प्रतिस्थापन का एक अप्रत्याशित विशेषता है। (आप इसके आस-पास काम कर सकते हैं, लेकिन फिर जवाब और भी जटिल हो जाता है।)
गिल्स एसओ- बुराई को रोकना '

6

सुनिश्चित नहीं है कि शेल बिल्डरों के साथ यह कैसे करना है ( Gnouc हालांकि है ), लेकिन मानक उपकरण मदद कर सकते हैं:

  1. आप wc -mवर्णों का उपयोग कर सकते हैं । दुर्भाग्य से, यह अंतिम नई पंक्ति को भी गिनता है ताकि आपको पहले उस से छुटकारा पाना पड़े:

    readlink -f /etc/fstab | tr -d '\n' | wc -m
  2. आप निश्चित रूप से उपयोग कर सकते हैं awk

    readlink -f /etc/fstab | awk '{print length($0)}'
  3. या पर्ल

    readlink -f /etc/fstab | perl -lne 'print length'

क्या आपका मतलब exprबिल्ट-इन है? किस खोल में?
15

5

मैं आमतौर पर इसे इस तरह से करता हूं:

$ echo -n "$variable" | wc -m
10

कमांड करने के लिए मैं इसे इस तरह से अनुकूलित करूंगा:

$ echo -n "$(readlink -f /etc/fstab)" | wc -m
10

यह दृष्टिकोण आपके 2 चरणों में आपके द्वारा किए जा रहे समान है, सिवाय इसके कि हम उन्हें एक सिंगल लाइनर में जोड़ रहे हैं।


2
आप का उपयोग करना चाहिए -mबजाय -c। यूनिकोड वर्णों के साथ, आपका दृष्टिकोण टूट जाएगा।
कोउंगलम

1
बस क्यों नहीं readlink -f /etc/fstab | wc -m?
फिल फ्रॉस्ट

1
आप इसके बजाय इस अविश्वसनीय विधि का उपयोग क्यों करते हैं ${#variable}? कम से कम दोहरे उद्धरण चिह्नों का उपयोग करें echo -n "$variable", लेकिन यह अभी भी विफल रहता है जैसे कि मान variableहै -e। जब आप एक कमांड प्रतिस्थापन के साथ संयोजन में इसका उपयोग करते हैं, तो ध्यान रखें कि अनुगामी newlines से छीन लिया जाता है।
गिल्स एसओ- बुराई को रोकना '

@philfrost b / c ने जो दिखाया उससे मुझे पता चलता है कि ऑप पहले से ही सोच रहा था। इसके अलावा यह किसी भी cmds के लिए काम करता है जिसे वह var में पहले सेटअप कर सकता है और अपनी लंबाई के बाद पासवर्ड चाहता है। इसके अलावा टेर्डन के पास वह उदाहरण है।
SLM

1

आप बाहरी उपयोगिताओं को कॉल कर सकते हैं (अन्य उत्तर देखें), लेकिन वे आपकी स्क्रिप्ट को धीमा कर देंगे, और प्लंबिंग अधिकार प्राप्त करना कठिन है।

Zsh

Zsh में, आप ${#$(readlink -f /etc/fstab)}कमांड प्रतिस्थापन की लंबाई प्राप्त करने के लिए लिख सकते हैं । ध्यान दें कि यह कमांड आउटपुट की लंबाई नहीं है, यह किसी भी नई ट्राइलिंग के बिना आउटपुट की लंबाई है।

यदि आप आउटपुट की सटीक लंबाई चाहते हैं, तो अंत में एक अतिरिक्त नॉन-न्यूलाइन चरित्र का उत्पादन करें, और एक को घटाएं।

$((${#$(readlink -f /etc/fstab; echo .)} - 1))

यदि आप जो चाहते हैं वह कमांड के आउटपुट में पेलोड है, तो आपको यहां दो को घटाना होगा , क्योंकि आउटपुट का readlink -fविहित पथ प्लस एक नई रेखा है।

$((${#$(readlink -f /etc/fstab; echo .)} - 2))

यह ${#$(readlink -f /etc/fstab)}दुर्लभ लेकिन संभव मामले से भिन्न होता है जहां विहित पथ स्वयं एक नई रेखा में समाप्त होता है।

इस विशिष्ट उदाहरण के लिए, आपको बाहरी उपयोगिता की आवश्यकता नहीं है, क्योंकि zsh में एक अंतर्निहित निर्माण है जो readlink -fइतिहास संशोधक के बराबर है A

echo /etc/fstab(:A)

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

${#${:-/etc/fstab}:A}

यदि आपके पास चर में फ़ाइल का नाम है filename, तो यह होगा ${#filename:A}

बॉर्न / POSIX- शैली के गोले

शुद्ध बोर्न / पोसिक्स गोले (बॉर्न, ऐश, मकश, ksh93, बैश, यश ...) में से कोई भी समान विस्तार नहीं है जो मुझे पता है। यदि आपको कमांड प्रतिस्थापन के आउटपुट या नेस्ट पैरामीटर प्रतिस्थापन के लिए एक पैरामीटर प्रतिस्थापन लागू करने की आवश्यकता है, तो क्रमिक चरणों का उपयोग करें।

यदि आप चाहें तो प्रोसेसिंग को एक फंक्शन में स्टफ कर सकते हैं।

command_output_length_sans_trailing_newlines () {
  set -- "$("$@")"
  echo "${#1}"
}

या

command_output_length () {
  set -- "$("$@"; echo .)"
  echo "$((${#1} - 1))"
}

लेकिन आमतौर पर कोई फायदा नहीं होता; ksh93 को छोड़कर, जो फ़ंक्शन के आउटपुट का उपयोग करने में सक्षम होने के लिए एक अतिरिक्त कांटा का कारण बनता है, इसलिए यह आपकी स्क्रिप्ट को धीमा कर देता है, और शायद ही कभी कोई पठनीयता लाभ होता है।

एक बार फिर, का उत्पादन readlink -fकैनोनिकल पथ और एक नई रेखा है; यदि आप विहित पथ की लंबाई चाहते हैं, तो 1 के बजाय 2 को घटाएं command_output_length। उपयोग करना command_output_length_sans_trailing_newlinesसही परिणाम देता है जब विहित पथ स्वयं एक नई रेखा में समाप्त नहीं होता है।

बाइट्स बनाम वर्ण

${#…}वर्णों में लंबाई माना जाता है, बाइट्स में नहीं, जो मल्टीबाइट स्थानों में अंतर करता है। Ksh93, bash और zsh के उचित रूप से अद्यतित संस्करण LC_CTYPEउस समय के मान के अनुसार वर्णों में लंबाई की गणना करते हैं , जब ${#…}निर्माण का विस्तार होता है। कई अन्य सामान्य गोले वास्तव में मल्टीबाइट स्थानों का समर्थन नहीं करते हैं: जैसे कि डैश 0.5.7, mksh 46 और पॉश 0.12.3, ${#…}बाइट्स में लंबाई लौटाता है। यदि आप पात्रों की लंबाई विश्वसनीय तरीके से चाहते हैं, तो wcउपयोगिता का उपयोग करें :

$(readlink -f /etc/fstab | wc -m)

जब तक $LC_CTYPEआप एक वैध लोकेल को डिज़ाइन करते हैं, तब तक आप आश्वस्त हो सकते हैं कि यह या तो त्रुटि देगा (एक प्राचीन या प्रतिबंधित प्लेटफ़ॉर्म जो मल्टीबाइट स्थानों का समर्थन नहीं करता है) या वर्णों में सही लंबाई लौटाता है। (यूनिकोड के लिए, "वर्णों में लंबाई" का अर्थ है कोड बिंदुओं की संख्या - ग्लिफ़ की संख्या अभी तक एक और कहानी है, जो कि पात्रों के संयोजन जैसी जटिलताओं के कारण है।)

यदि आप बाइट्स में लंबाई चाहते हैं, तो LC_CTYPE=Cअस्थायी रूप से सेट करें , या wc -cइसके बजाय का उपयोग करें wc -m

बाइट्स या वर्णों की गिनती wcकमांड के किसी भी अनुगामी newlines में शामिल है। यदि आप बाइट्स में विहित पथ की लंबाई चाहते हैं, तो यह है

$(($(readlink -f /etc/fstab | wc -c) - 1))

वर्णों में प्राप्त करने के लिए, 2 को घटाएं।


@cuonglm नहीं, आपको 1 को घटाना होगा। echo .दो वर्णों को जोड़ा जाता है, लेकिन दूसरा वर्ण एक अनुगामी न्यूलाइन है जिसे कमांड प्रतिस्थापन द्वारा छीन लिया जाता है।
गिल्स एसओ- बुराई को रोकना '

न्यू लाइन से है readlinkउत्पादन, प्लस .द्वारा echo। हम दोनों सहमत हैं कि echo .दो अक्षर जोड़ते हैं, लेकिन अनुगामी न्यूलाइन छीन ली गई। printf .मेरे उत्तर unix.stackexchange.com/a/160499/38906 के साथ देखें या देखें ।
cuonglm

@cuonglm प्रश्न ने कमांड के आउटपुट में वर्णों की संख्या पूछी। इसका आउटपुट readlinkलिंक लक्ष्य और एक नई रेखा है।
गिल्स एसओ- बुराई को रोकें '17

0

यह काम करता है, dashलेकिन यह आवश्यक है कि लक्षित संस्करण निश्चित रूप से खाली या परेशान न हो। यही कारण है कि यह वास्तव में दो आदेश हैं - मैं स्पष्ट रूप $lसे पहले में खाली हूं :

l=;printf '%.slen is %d and result is %s\n' \
    "${l:=$(readlink -f /etc/fstab)}" "${#l}" "$l"

आउटपुट

len is 10 and result is /etc/fstab

यह सभी शेल बिल्डिंस हैं - readlinkबेशक शामिल नहीं हैं - लेकिन मौजूदा शेल में इसका मूल्यांकन इस तरह से है कि इसका मतलब है कि आपको लेन प्राप्त करने से पहले असाइनमेंट करना होगा, यही कारण है कि मैं प्रारूप स्ट्रिंग %.sमें पहला तर्क देता हूं printfऔर बस इसे फिर से जोड़ता हूं शाब्दिक मूल्य printfसूची की पूंछ पर है ।

के साथ eval:

l=$(readlink -f /etc/fstab) eval 'l=${#l}:$l'
printf %s\\n "$l"

आउटपुट

10:/etc/fstab

आप उसी चीज़ के करीब पहुँच सकते हैं, लेकिन पहले कमांड में एक चर में आउटपुट के बजाय आप इसे स्टडआउट पर प्राप्त करते हैं:

PS4='${#0}:$0' dash -cx '2>&1' "$(readlink -f /etc/fstab)"

... जो लिखता है ...

10:/etc/fstab

... वर्तमान शेल में किसी भी संस्करण के लिए कोई मान निर्दिष्ट किए बिना विवरणक 1 दाखिल करने के लिए।


1
क्या ओपी इससे बचना चाहता था? "मैं समझता हूं कि पहले आउटपुट को एक चर में सहेजकर ऐसा करना संभव है: variable=$(readlink -f /etc/fstab); echo ${#variable};लेकिन मैं अतिरिक्त कदम को दूर करना चाहूंगा।"
terdon

@terdon, शायद मैंने गलत समझा, लेकिन यह मेरी धारणा थी कि अर्धविराम समस्या थी और परिवर्तनशील नहीं। यही कारण है कि ये केवल शेल बिल्डरों का उपयोग करके एक एकल सरल कमांड में लेन और आउटपुट प्राप्त करते हैं। खोल नहीं कार्यकारी readlink करता है तो कार्यकारी exprउदाहरण के लिए,। यह शायद केवल तभी मायने रखता है जब किसी भी तरह से लेन का मूल्य कम हो जाता है, जिसे मैं मानता हूं कि मुझे यह समझने में कठिनाई हो रही है कि ऐसा क्यों हो सकता है, लेकिन मुझे संदेह है कि इसमें एक मामला हो सकता है जिसमें यह मामला था।
15

1
evalजिस तरह से, वैसे, शायद यहाँ साफ है - - यह उत्पादन और एक एकल निष्पादन में एक ही वार नाम के लेन प्रदान करती है बहुत कर के करीब l=length(l):out(l)। कर expr length $(command) करता है लेन के पक्ष में मूल्य को ढंक लेते हैं, वैसे।
15
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.