bash मैं बैश में चर द्वारा परिभाषित संख्याओं की एक श्रृंखला पर कैसे पुन: प्रयास करूं?




खाद्य श्रृंखला व खाद्य जाल (14)

जब सीमा एक चर द्वारा दी जाती है तो मैं बैश में कितनी संख्याओं को फिर से चला सकता हूं?

मुझे पता है कि मैं यह कर सकता हूं (बैश documentation में "अनुक्रम अभिव्यक्ति" कहा जाता है):

 for i in {1..5}; do echo $i; done

जो देता है:

1
2
3
4
5

फिर भी, मैं एक चर के साथ सीमा सीमा अंतराल को कैसे बदल सकता हूं? यह काम नहीं करता है:

END=5
for i in {1..$END}; do echo $i; done

कौन से प्रिंट:

{} 1..5


for i in $(seq 1 $END); do echo $i; done

संपादित करें: मैं अन्य तरीकों से seq पसंद करता हूं क्योंकि मैं वास्तव में इसे याद कर सकता हूं;)


seq विधि सबसे सरल है, लेकिन बैश ने अंकगणितीय मूल्यांकन में अंतर्निहित किया है।

END=5
for ((i=1;i<=END;i++)); do
    echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines

के for ((expr1;expr2;expr3)); सी और इसी तरह की भाषाओं में for (expr1;expr2;expr3) तरह काम करता है, और अन्य ((expr)) मामलों की तरह, बैश उन्हें अंकगणितीय के रूप में व्यवहार करता है।


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

for i in $(seq $END); do echo $i; done

{} साथ बदलें (( )) :

tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i<=$tmpend; i++ )) ; do 
echo $i ;
done

पैदावार:

0
1
2
3
4

यह बैश और कॉर्न में काम करता है, यह भी उच्च से निम्न संख्या तक जा सकता है। शायद सबसे तेज़ या सुंदर नहीं है लेकिन काफी अच्छी तरह से काम करता है। नकारात्मक भी संभालती है।

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} <= ${e} )); do
         echo ${v}
         ((v=v+1))
      done
   elif (( ${e} < ${s} )); then
      v=${s}
      while (( ${v} >= ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}

यदि आप ब्रेस-एक्सप्रेशन सिंटैक्स के लिए जितना संभव हो उतना करीब रहना चाहते हैं, तो बैश-ट्रिक्स ' range.bash से range फ़ंक्शन को range.bash

उदाहरण के लिए, निम्नलिखित सभी echo {1..10} जैसी सटीक चीज़ करेंगे:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

यह देशी बैश सिंटैक्स को जितना संभव हो उतना "गॉथचास" के साथ समर्थन करने का प्रयास करता है: न केवल वेरिएबल्स समर्थित हैं, बल्कि अमान्य श्रेणियों के अक्सर अवांछित व्यवहार को तारों के रूप में आपूर्ति की जा रही है (उदाहरण के for i in {1..a}; do echo $i; done ) भी रोका गया है।

अन्य उत्तर ज्यादातर मामलों में काम करेंगे, लेकिन उनमें से सभी में निम्न में से कम से कम एक दोष है:

  • उनमें से कई subshells उपयोग करते हैं, जो प्रदर्शन को नुकसान पहुंचा सकता है और कुछ सिस्टम पर संभव नहीं हो सकता है
  • उनमें से कई बाहरी कार्यक्रमों पर भरोसा करते हैं। यहां तक ​​कि seq एक बाइनरी है जिसे इस्तेमाल करने के लिए स्थापित किया जाना चाहिए, बैश द्वारा लोड किया जाना चाहिए, और इस मामले में काम करने के लिए आपके द्वारा अपेक्षित कार्यक्रम होना चाहिए। सर्वव्यापी या नहीं, यह केवल बैश भाषा की तुलना में भरोसा करने के लिए बहुत कुछ है।
  • समाधान जो केवल मूल बैश कार्यक्षमता का उपयोग करते हैं, जैसे @ एपैमेन्टेंट, वर्णमाला श्रेणियों पर काम नहीं करेंगे, जैसे {a..z} ; ब्रेस विस्तार होगा। प्रश्न संख्याओं की सीमाओं के बारे में था, हालांकि, यह एक quibble है।
  • उनमें से अधिकतर {1..10} ब्रेस-विस्तारित रेंज वाक्यविन्यास के समान दृश्यमान नहीं हैं, इसलिए दोनों प्रोग्राम जो पढ़ने के लिए कठिन हो सकते हैं।
  • @ बॉबबोगो का उत्तर कुछ परिचित वाक्यविन्यास का उपयोग करता है, लेकिन कुछ अनपेक्षित करता है यदि $END चर रेंज के दूसरी तरफ एक वैध सीमा "बुकेंड" नहीं है। यदि END=a , उदाहरण के लिए, कोई त्रुटि नहीं होगी और verbatim मान {1..a} प्रतिबिंबित किया जाएगा। यह बैश का डिफ़ॉल्ट व्यवहार भी है - यह अक्सर अनपेक्षित होता है।

अस्वीकरण: मैं लिंक किए गए कोड का लेखक हूं।


ये सभी अच्छे हैं लेकिन सीईसी को माना जाता है और अधिकांश संख्यात्मक संख्याओं के साथ काम करते हैं।

यदि आप डबल कोट्स में लूप के लिए संलग्न करते हैं, तो जब आप स्ट्रिंग को प्रतिबिंबित करते हैं तो स्टार्ट एंड एंड वेरिएबल्स को डिफ्रेंस किया जाएगा, और आप स्ट्रिंग को निष्पादन के लिए वापस BASH पर भेज सकते हैं। $i बचने की जरूरत है इसलिए इसे सबहेल पर भेजने से पहले मूल्यांकन नहीं किया जाता है।

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

यह आउटपुट एक चर को भी असाइन किया जा सकता है:

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

इसे उत्पन्न करने वाला एकमात्र "ओवरहेड" बैश का दूसरा उदाहरण होना चाहिए ताकि यह गहन संचालन के लिए उपयुक्त हो।


यदि आप बीएसडी / ओएस एक्स पर हैं तो आप सीक के बजाय जोट का उपयोग कर सकते हैं:

for i in $(jot $END); do echo $i; done

यदि आपको इसे पसंद करने की तुलना में उपसर्ग की आवश्यकता है

 for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done

वह उपज होगा

07
08
09
10
11
12

विचार-विमर्श

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

पूर्णांक अंकगणितीय

यह बैश लूप का एक बेहतर संस्करण है:

typeset -i i END
let END=5 i=1
while ((i<=END)); do
    echo $i
    …
    let i++
done

यदि एकमात्र चीज जो हम चाहते हैं वह echo , तो हम echo $((i++)) लिख सकते हैं echo $((i++))

ephemient ने मुझे कुछ सिखाया: बैश for ((expr;expr;expr)) अनुमति देता है। चूंकि मैंने कभी भी बैश के लिए पूरे आदमी पृष्ठ को नहीं पढ़ा है (जैसे मैंने कॉर्न शैल ( ksh ) मैन पेज के साथ किया है, और यह बहुत समय पहले था), मुझे याद आया।

इसलिए,

typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done

लगता है कि सबसे मेमोरी-कुशल तरीका है ( seq के आउटपुट का उपभोग करने के लिए स्मृति आवंटित करना आवश्यक नहीं होगा, अगर ईएनडी बहुत बड़ा हो तो समस्या हो सकती है), हालांकि शायद "सबसे तेज़" नहीं है।

प्रारंभिक प्रश्न

एस्चेरसायकल ने नोट किया कि { .. बी } बैश नोटेशन केवल शाब्दिक के साथ काम करता है; बाश मैनुअल के अनुसार, सच है। कोई भी एक (आंतरिक) fork() बिना एक exec() fork() बिना इस बाधा को दूर कर सकता है (जैसा कि कॉलिंग के साथ मामला है, जिसे एक और छवि के लिए एक कांटा + निष्पादन की आवश्यकता होती है):

for i in $(eval echo "{1..$END}"); do

दोनों eval और echo बैश बिल्टिन हैं, लेकिन कमांड प्रतिस्थापन ( $(…) निर्माण के लिए एक fork() आवश्यक है।


यह एक और तरीका है:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done

पॉज़िक्स तरीका

यदि आप पोर्टेबिलिटी की परवाह करते हैं, तो POSIX मानक से उदाहरण का उपयोग करें:

i=2
end=5
while [ $i -le $end ]; do
    echo $i
    i=$(($i+1))
done

आउटपुट:

2
3
4
5

चीजें जो POSIX नहीं हैं:


यदि आप खोल कमांड कर रहे हैं और आप (जैसे मैं) पाइपलाइनिंग के लिए एक बुत है, यह एक अच्छा है:

seq 1 $END | xargs -I {} echo {}


यही कारण है कि मूल अभिव्यक्ति काम नहीं कर सका।

मैन बैश से :

ब्रेस विस्तार किसी भी अन्य विस्तार से पहले किया जाता है, और परिणामस्वरूप अन्य विस्तारों के लिए विशेष पात्रों को संरक्षित किया जाता है। यह कड़ाई से पाठ्यचर्या है। बैश विस्तार के संदर्भ या ब्रेसिज़ के बीच के पाठ के संदर्भ में कोई वाक्य रचनात्मक व्याख्या लागू नहीं करता है।

तो, पैरामीटर विस्तार से पहले, ब्रेस विस्तार कुछ पूरी तरह से पाठ मैक्रो ऑपरेशन के रूप में जल्दी किया जाता है

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

सिफ़ारिश करना

मैं पॉज़िक्स 1 फीचर्स के साथ चिपकने का सुझाव दूंगा। इसका मतलब है कि for i in <list>; do उपयोग कर for i in <list>; do अगर, सूची पहले ही ज्ञात है, अन्यथा, while या seq उपयोग करें, जैसा कि:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done

1. बैश एक महान खोल है और मैं इसे इंटरैक्टिव रूप से उपयोग करता हूं, लेकिन मैं अपनी स्क्रिप्ट में बैश-इम्स नहीं डालता हूं। लिपियों को एक तेज खोल की आवश्यकता हो सकती है, एक और अधिक सुरक्षित, एक अधिक एम्बेडेड-शैली वाला एक। उन्हें जो भी / bin / sh के रूप में स्थापित किया गया है, उसे चलाने की आवश्यकता हो सकती है, और उसके बाद सभी सामान्य प्रो-मानकों के तर्क होते हैं। शेलशॉक , उर्फ बैशडोर याद रखें ?







shell