बैश में ट्यूपल्स पर लूप?


88

क्या बैश में ट्यूपल्स पर लूप करना संभव है?

एक उदाहरण के रूप में, यह बहुत अच्छा होगा यदि निम्नलिखित काम करें:

for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done

वहाँ एक समाधान है कि किसी तरह मुझे tuples पर पाश है?


4
अजगर पृष्ठभूमि से आ रहा है यह वास्तव में एक बहुत ही उपयोगी प्रश्न है!
जॉन जियांग

5
यह देखकर चार साल बाद मुझे आश्चर्य हुआ कि क्या अभी भी ऐसा करने का कोई बेहतर तरीका नहीं है। हे भगवान।
गिज़्मो

लगभग 8 साल बाद मुझे भी आश्चर्य हुआ कि क्या अब भी ऐसा करने का कोई बेहतर तरीका नहीं है। : लेकिन यह 2018 जवाब मेरे लिए बहुत अच्छा लग रहा है stackoverflow.com/a/52228219/463994
MountainX

जवाबों:


87
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5

इसके set(से man builtins) उपयोग के बारे में :

विकल्प प्रसंस्करण के बाद शेष किसी भी तर्क को स्थितिगत मापदंडों के लिए मान के रूप में माना जाता है और इसे $ 1, $ 2, ... के क्रम में सौंपा जाता है।

IFS=","क्षेत्र विभाजक तो हर सेट $iमें ही खंडित हो जाता है $1और $2सही ढंग से।

इस ब्लॉग के माध्यम से ।

संपादित करें: @SLACEDIAMOND द्वारा सुझाए गए अनुसार अधिक सही संस्करण:

$ OLDIFS=$IFS; IFS=','; for i in c,3 e,5; do set -- $i; echo $1 and $2; done; IFS=$OLDIFS
c and 3
e and 5

7
अच्छा - IFSअगर यह इंगित करना है कि कमांड लाइन पर चलाया जाता है तो बस सहेजा जाना चाहिए और अपने मूल मूल्य पर रीसेट करना चाहिए। इसके अलावा, नया IFSएक बार सेट किया जा सकता है, लूप के चलने से पहले, हर पुनरावृत्ति के बजाय।
अंडगैक्टली

1
मामले में किसी भी $ मैं एक हाइफ़न के साथ शुरू होता है, यह सुरक्षित हैset -- $i
ग्लेन जैकमैन

1
बचत करने के बजाय IFS, केवल इसे setकमांड के लिए सेट करें for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done:। कृपया अपना उत्तर संपादित करें: यदि सभी पाठक सूचीबद्ध समाधानों में से केवल एक का चयन करेंगे, तो पूर्ण विकास इतिहास को पढ़ने में कोई समझदारी नहीं है। इस शांत चाल के लिए धन्यवाद!
cfc

यदि मैं घोषित संस्करण में घोषित करता हूं tuples="a,1 b,2 c,3"और उपयोग करता हूं IFS=',', और c,3 e,5उपयोग के बजाय $tuplesयह बिल्कुल भी प्रिंट नहीं करता है। लेकिन इसके बजाय अगर मैं IFS=','केवल doलूप के लिए कीवर्ड के बाद रखता हूं , तो यह अच्छी $tuplesतरह से और साथ ही साथ लैटरल मानों का उपयोग करते समय काम करता है। बस यह कहने लायक था।
साइमनब्लक

@Simonlbc ऐसा इसलिए है क्योंकि लूप के लिए IFSपुनरावृत्तियों को विभाजित करने के लिए उपयोग करता है। यदि आप किसी सरणी पर लूप करते हैं, जैसे कि लूप के लिए arr=("c,3" "e,5")रखा जाता है IFS, तो $iवसीयत का मूल्य बस cऔर eहोगा, यह अलग हो जाएगा 3और 5इसलिए setसही तरीके से पार्स $iनहीं होगा क्योंकि पार्स करने के लिए कुछ भी नहीं होगा। इसका मतलब यह है कि अगर इट्रेट करने के मूल्यों को इनलेट नहीं किया जाता है, तो IFSउन्हें लूप के अंदर रखा जाना चाहिए, और बाहरी मान वेरिएबल के लिए इच्छित विभाजक का सम्मान करना चाहिए। इसके मामलों में $tuplesबस होना चाहिए IFS=जो डिफ़ॉल्ट है और व्हॉट्सएप पर विभाजित होता है।
untore

25

मेरा मानना ​​है कि यह समाधान दूसरों की तुलना में थोड़ा क्लीनर है, जो कि इस बैश स्टाइल गाइड के लिए एच / टी को दिखाता है कि कैसे पढ़ने के लिए एक सीमांकक पर स्ट्रिंग्स को विभाजित करने और उन्हें अलग-अलग चर को असाइन करने के लिए उपयोग किया जा सकता है।

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done

17

सेटिंग / रीसेट किए बिना @ eduardo-ivanec द्वारा दिए गए उत्तर के आधार पर IFS, कोई भी बस कर सकता है:

for i in "c 3" "e 5"
do
    set -- $i
    echo $1 and $2
done

उत्पादन:

c and 3
e and 5

यह दृष्टिकोण मुझे स्वीकृत और सबसे अधिक अप्रोच किए गए दृष्टिकोण की तुलना में बहुत सरल लगता है। क्या कोई कारण नहीं है कि इसे इस तरह न किया जाए जैसा कि @Eduardo Ivanec ने विरोध किया था?
spurra

@spurra यह उत्तर 6 साल और and अधिक हाल का है, और इस पर आधारित है। क्रेडिट जहां यह कारण है।
डिएगो

1
@Diego मैं इसके बारे में पता कर रहा हूँ। यह स्पष्ट रूप से उत्तर में लिखा गया है। मैं पूछ रहा था कि क्या कोई कारण है जो स्वीकृत उत्तर पर इस दृष्टिकोण का उपयोग नहीं करता है।
spurra

2
अगर आप डिफॉल्ट सेपरेटर (स्पेस, टैब या न्यूलाइन) किसी कारण से आपके लिए काम नहीं करते हैं तो आप @spurra का उपयोग करना चाहते हैं ( bash.cyberciti.biz/guide/$IFS )
डिएगो

11

साहचर्य सरणी का उपयोग करें (इसे शब्दकोश / हैशपॉप के रूप में भी जाना जाता है):

declare -A pairs=(
  [c]=3
  [e]=5
)
for key in "${!pairs[@]}"; do
  value="${pairs[$key]}"
  echo "key is $key and value is $value"
done

Bash4.0 + के लिए काम करता है।


यदि आपको जोड़े के बजाय त्रिकोणीय की आवश्यकता है, तो आप अधिक सामान्य दृष्टिकोण का उपयोग कर सकते हैं:

animals=(dog cat mouse)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
for animal in "${animals[@]}"; do
  echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done

FYI करें, यह मेरे लिए मैक पर काम नहीं करता GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)था, जो कि काढ़ा के माध्यम से स्थापित किया गया था, इसलिए YMMV।
डेविड

हालांकि इसने GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)docker कंटेनर के भीतर Ubuntu 14.04 से काम किया ।
डेविड

लगता है जैसे बैश के पुराने संस्करण या जो इस सुविधा का समर्थन नहीं कर रहे हैं वे इंडेक्स बेसिंग से काम करते हैं? जहां कुंजी स्ट्रिंग के बजाय एक संख्या है। tldp.org/LDP/abs/html/declareref.html , और इसके बजाय -Aहमारे पास है -a
डेविड

डेविड, ऐसा लगता है। मुझे लगता है कि आप तब "समरूपता" प्राप्त करने के लिए सरणी सूचकांकों की कोशिश कर सकते हैं। declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)आदि की तरह यह अभी तक टर्मिनल में कोशिश नहीं की है, लेकिन मुझे लगता है कि यह काम करना चाहिए।
VasiliNovikov

7
c=('a' 'c')
n=(3    4 )

for i in $(seq 0 $((${#c[*]}-1)))
do
    echo ${c[i]} ${n[i]}
done

कभी-कभी अधिक काम आ सकता है।

uglyभाग को समझाने के लिए , जैसा कि टिप्पणियों में दिया गया है:

seq 0 2 संख्याओं के अनुक्रम का उत्पादन करता है 0 1 2. $ (cmd) कमांड प्रतिस्थापन है, इसलिए इस उदाहरण के लिए आउटपुट है seq 0 2, जो संख्या अनुक्रम है। लेकिन क्या ऊपरी बाध्य है $((${#c[*]}-1))?

$ ((कुछ)) अंकगणितीय विस्तार है, इसलिए $ ((3 + 4)) 7 है। हमारी अभिव्यक्ति है ${#c[*]}-1, इसलिए कुछ - 1. बहुत सरल, अगर हम जानते हैं कि क्या ${#c[*]}है।

c एक सरणी है, c [*] सिर्फ संपूर्ण सरणी है, $ {# c [*]} सरणी का आकार है जो हमारे मामले में 2 है। अब हम सब कुछ वापस रोल: for i in $(seq 0 $((${#c[*]}-1)))है for i in $(seq 0 $((2-1)))है for i in $(seq 0 1)है for i in 0 1। क्योंकि एरे में अंतिम तत्व का एक इंडेक्स है जो एरे की लंबाई है - 1।


1
आपको करना चाहिएfor i in $(seq 0 $(($#c[*]}-1))); do [...]
पुनः लोड करें

1
वाह, यह "अग्लीस्टेंट बंच ऑफ आर्बिटवर्ल्ड कैरेक्टर्स आई सीन सीन टु" पुरस्कार जीतता है। किसी को भी यह समझाने की परवाह है कि यह घृणा वास्तव में क्या करती है? मैं हैश साइन में खो गया ...
koniiiik

1
@koniiiik: स्पष्टीकरण जोड़ा गया।
उपयोगकर्ता अज्ञात


3

GNU समानांतर का उपयोग:

parallel echo {1} and {2} ::: c e :::+ 3 5

या:

parallel -N2 echo {1} and {2} ::: c 3 e 5

या:

parallel --colsep , echo {1} and {2} ::: c,3 e,5

1
इसके लिए कोई प्यार नहीं? अच्छी तरह से मुझे जड़ता से उबरने और स्थापित करनेgnu parallel
स्टीफनबॉश

2
brew install parallel
StephenBoesch

2

printfएक प्रक्रिया प्रतिस्थापन में उपयोग करना :

while read -r k v; do
    echo "Key $k has value: $v"
done < <(printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3')

Key key1 has value: val1
Key key2 has value: val2
Key key3 has value: val3

ऊपर की आवश्यकता है bash। यदि bashउपयोग नहीं किया जा रहा है तो सरल पाइपलाइन का उपयोग करें:

printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3' |
while read -r k v; do echo "Key $k has value: $v"; done

1
हाँ! अनुभा, आप प्यारी प्रतिभा!
स्कॉट

1
do echo $key $value
done < file_discriptor

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

$ while read key value; do echo $key $value ;done <<EOF
> c 3
> e 5
> EOF
c 3
e 5

$ echo -e 'c 3\ne 5' > file

$ while read key value; do echo $key $value ;done <file
c 3
e 5

$ echo -e 'c,3\ne,5' > file

$ while IFS=, read key value; do echo $key $value ;done <file
c 3
e 5


0

लेकिन क्या होगा यदि ट्यूपल k / v से अधिक है जो एक साहचर्य सरणी पकड़ सकता है? यदि यह 3 या 4 तत्व है तो क्या होगा? इस अवधारणा पर विस्तार किया जा सकता है:

###---------------------------------------------------
### VARIABLES
###---------------------------------------------------
myVars=(
    'ya1,ya2,ya3,ya4'
    'ye1,ye2,ye3,ye4'
    'yo1,yo2,yo3,yo4'
    )


###---------------------------------------------------
### MAIN PROGRAM
###---------------------------------------------------
### Echo all elements in the array
###---
printf '\n\n%s\n' "Print all elements in the array..."
for dataRow in "${myVars[@]}"; do
    while IFS=',' read -r var1 var2 var3 var4; do
        printf '%s\n' "$var1 - $var2 - $var3 - $var4"
    done <<< "$dataRow"
done

तब आउटपुट कुछ इस तरह दिखाई देगा:

$ ./assoc-array-tinkering.sh 

Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4

और तत्वों की संख्या अब सीमा के बिना है। वोट की तलाश में नहीं; बस जोर से सोच रहा था। आरईएफ 1 , आरईएफ 2


0

ऐसे मामलों में जहां मेरी तुच्छ परिभाषाएं अधिक जटिल हैं, मैं उन्हें एक हेरेडोक में रखना पसंद करता हूं:

while IFS=", " read -ra arr; do
  echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM

यह कुछ वांछित पृथक्करण वर्ण पर रेखाओं को विभाजित करने के साथ एक हेरेडोक की लाइनों पर लूपिंग को जोड़ती है ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.