bash - खाद्य श्रृंखला व खाद्य जाल




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

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

मुझे पता है कि मैं यह कर सकता हूं (बैश 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


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

सीआक का उपयोग ठीक है, जैसा कि जियारो ने सुझाव दिया था। पैक्स डायब्लो ने एक सबप्रोसेस को कॉल करने से बचने के लिए एक बैश लूप का सुझाव दिया, यदि $ 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() आवश्यक है।


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

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

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

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


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

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

यह bash में ठीक काम करता है:

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

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

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"
}

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

मैन बैश से :

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

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

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

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

मैं पॉज़िक्स 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 के रूप में स्थापित किया गया है, उसे चलाने की आवश्यकता हो सकती है, और उसके बाद सभी सामान्य प्रो-मानकों के तर्क होते हैं। शेलशॉक , उर्फ बैशडोर याद रखें ?


संकेत की एक और परत:

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

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)) मामलों की तरह, बैश उन्हें अंकगणितीय के रूप में व्यवहार करता है।


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

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

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

आउटपुट:

2
3
4
5

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


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

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





shell