Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Bash प्रोग्रामिंग

Bash Arrays में मास्टरी: कमांड लाइन उत्साही लोगों के लिए एक व्यावहारिक गाइड

हालांकि सॉफ्टवेयर इंजीनियर विकास के कई पहलुओं के लिए कमांड लाइन का नियमित रूप से उपयोग करते हैं, arrays शायद कमांड लाइन की अधिक अस्पष्ट विशेषताओं में से एक हैं (हालांकि regex ऑपरेटर =~ जितना अस्पष्ट नहीं)। लेकिन अस्पष्टता और संदिग्ध सिंटैक्स के बावजूद, Bash arrays बहुत शक्तिशाली हो सकते हैं।

रुको, लेकिन क्यों?

Bash के बारे में लिखना चुनौतीपूर्ण है क्योंकि किसी लेख का मैनुअल में विकसित होना उल्लेखनीय रूप से आसान है जो सिंटैक्स विषमताओं पर केंद्रित है। आश्वस्त रहें, हालांकि, इस लेख का इरादा आपको RTFM से बचाना है।

एक वास्तविक (वास्तव में उपयोगी) उदाहरण

उस अंत तक, आइए एक वास्तविक दुनिया के परिदृश्य पर विचार करें और कैसे Bash मदद कर सकता है: आप अपनी कंपनी में अपने आंतरिक डेटा पाइपलाइन के रनटाइम का मूल्यांकन और अनुकूलन करने के लिए एक नए प्रयास का नेतृत्व कर रहे हैं। पहले चरण के रूप में, आप एक पैरामीटर स्वीप करना चाहते हैं ताकि यह मूल्यांकन किया जा सके कि पाइपलाइन थ्रेड्स का कितना अच्छा उपयोग करती है। सरलता के लिए, हम पाइपलाइन को एक कंपाइल्ड C++ ब्लैक बॉक्स के रूप में मानेंगे जहां हम केवल एक पैरामीटर को ट्वीक कर सकते हैं वह है डेटा प्रोसेसिंग के लिए आरक्षित थ्रेड्स की संख्या: ./pipeline --threads 4।

बुनियादी बातें

पहली चीज जो हम करेंगे वह है --threads पैरामीटर के उन मानों को समाहित करने वाला एक array परिभाषित करना जिन्हें हम टेस्ट करना चाहते हैं:

allThreads=(1 2 4 8 16 32 64 128)

इस उदाहरण में, सभी तत्व संख्याएं हैं, लेकिन ऐसा होना जरूरी नहीं है—Bash में arrays संख्याओं और स्ट्रिंग्स दोनों को समाहित कर सकते हैं, उदाहरण के लिए, myArray=(1 2 "three" 4 "five") एक वैध अभिव्यक्ति है। और किसी भी अन्य Bash वेरिएबल की तरह, बराबर चिह्न के चारों ओर कोई स्पेस न छोड़ें। अन्यथा, Bash वेरिएबल नाम को निष्पादित करने के लिए एक प्रोग्राम के रूप में मानेगा, और = को इसका पहला पैरामीटर!

अब जब हमने array को इनिशियलाइज़ कर लिया है, तो आइए इसके कुछ तत्वों को पुनः प्राप्त करें। आप देखेंगे कि केवल echo $allThreads करने से केवल पहला तत्व आउटपुट होगा।

यह समझने के लिए कि ऐसा क्यों है, आइए एक कदम पीछे हटें और फिर से देखें कि हम आमतौर पर Bash में वेरिएबल्स को कैसे आउटपुट करते हैं। निम्न परिदृश्य पर विचार करें:

type="article"
echo "Found 42 $type"

मान लें कि वेरिएबल $type हमें एकवचन संज्ञा के रूप में दिया गया है और हम अपने वाक्य के अंत में एक s जोड़ना चाहते हैं। हम केवल $type में s नहीं जोड़ सकते क्योंकि यह इसे एक अलग वेरिएबल, $types में बदल देगा। और यद्यपि हम echo "Found 42 "$type"s" जैसे कोड विकृतियों का उपयोग कर सकते हैं, इस समस्या को हल करने का सबसे अच्छा तरीका कर्ली ब्रेसेस का उपयोग करना है: echo "Found 42 ${type}s", जो हमें Bash को यह बताने की अनुमति देता है कि वेरिएबल का नाम कहां शुरू और समाप्त होता है (दिलचस्प है, यह वही सिंटैक्स है जो JavaScript/ES6 में टेम्पलेट लिटरल्स में वेरिएबल्स और एक्सप्रेशन्स को इंजेक्ट करने के लिए उपयोग किया जाता है)।

तो जैसा कि यह पता चलता है, हालांकि Bash वेरिएबल्स को आमतौर पर कर्ली ब्रैकेट्स की आवश्यकता नहीं होती, वे arrays के लिए आवश्यक हैं। बदले में, यह हमें एक्सेस करने के लिए इंडेक्स निर्दिष्ट करने की अनुमति देता है, उदाहरण के लिए, echo ${allThreads[1]} array का दूसरा तत्व लौटाता है। ब्रैकेट्स शामिल न करने पर, उदाहरण के लिएecho $allThreads[1], Bash [1] को एक स्ट्रिंग मानता है और इसे ऐसे ही आउटपुट करता है।

हां, Bash arrays का अजीब सिंटैक्स है, लेकिन कम से कम वे शून्य-अनुक्रमित हैं, कुछ अन्य भाषाओं के विपरीत (मैं आपको देख रहा हूं, R)।

Arrays के माध्यम से लूप करना

हालांकि ऊपर के उदाहरणों में हमने अपने arrays में पूर्णांक इंडेक्स का उपयोग किया, आइए उन दो अवसरों पर विचार करें जब ऐसा नहीं होगा: पहला, यदि हम array का $i-वां तत्व चाहते थे, जहां $i रुचि के इंडेक्स को समाहित करने वाला एक वेरिएबल है, हम उस तत्व को पुनः प्राप्त कर सकते हैं: echo ${allThreads[$i]}। दूसरा, array के सभी तत्वों को आउटपुट करने के लिए, हम न्यूमेरिक इंडेक्स को @ सिंबल से बदल देते हैं (आप @ को all के लिए खड़ा मान सकते हैं): echo ${allThreads[@]}।

Array तत्वों के माध्यम से लूप करना

इसे ध्यान में रखते हुए, आइए $allThreads के माध्यम से लूप करें और --threads के प्रत्येक मान के लिए पाइपलाइन लॉन्च करें:

for t in ${allThreads[@]}; do
./pipeline --threads $t
done

Array इंडेक्सेस के माध्यम से लूप करना

अब, आइए थोड़ा अलग दृष्टिकोण पर विचार करें। Array तत्वों पर लूप करने के बजाय, हम array इंडेक्सेस पर लूप कर सकते हैं:

for i in ${!allThreads[@]}; do
./pipeline --threads ${allThreads[$i]}
done

इसे तोड़ते हैं: जैसा कि हमने ऊपर देखा, ${allThreads[@]} हमारे array में सभी तत्वों का प्रतिनिधित्व करता है। एक विस्मयादिबोधक चिह्न जोड़कर इसे ${!allThreads[@]} बनाने से सभी array इंडेक्सेस की सूची वापस मिल जाएगी (हमारे मामले में 0 से 7)। दूसरे शब्दों में, for लूप सभी इंडेक्सेस $i के माध्यम से लूप कर रहा है और $allThreads से $i-वां तत्व पढ़कर --threads पैरामीटर का मान सेट कर रहा है।

यह आंखों पर बहुत कठोर है, तो आप सोच रहे होंगे कि मैं इसे पहली जगह में पेश करने की जहमत क्यों उठा रहा हूं। ऐसा इसलिए है क्योंकि ऐसे समय होते हैं जब आपको लूप के भीतर इंडेक्स और मान दोनों जानने की आवश्यकता होती है, उदाहरण के लिए, यदि आप array के पहले तत्व को अनदेखा करना चाहते हैं, तो इंडेक्सेस का उपयोग आपको एक अतिरिक्त वेरिएबल बनाने से बचाता है जिसे आप फिर लूप के अंदर बढ़ाते हैं।

Arrays को पॉप्युलेट करना

अब तक, हम अपने इंटरेस्ट के प्रत्येक --threads के लिए पाइपलाइन लॉन्च करने में सक्षम हैं। अब, मान लें कि हमारी पाइपलाइन का आउटपुट सेकंड में रनटाइम है। हम उस आउटपुट को प्रत्येक पुनरावृत्ति पर कैप्चर करना चाहेंगे और इसे दूसरे array में सहेजना चाहेंगे ताकि हम अंत में इसके साथ विभिन्न हेरफेर कर सकें।

कुछ उपयोगी सिंटैक्स

लेकिन कोड में गोता लगाने से पहले, हमें कुछ और सिंटैक्स पेश करने की आवश्यकता है। पहला, हमें Bash कमांड के आउटपुट को पुनः प्राप्त करने में सक्षम होना चाहिए। ऐसा करने के लिए, निम्न सिंटैक्स का उपयोग करें: output=$( ./my_script.sh ), जो हमारे कमांड्स के आउटपुट को वेरिएबल $output में स्टोर करेगा।

दूसरा सिंटैक्स जिसकी हमें आवश्यकता है वह है केवल पुनर्प्राप्त किए गए मान को array में जोड़ना। ऐसा करने का सिंटैक्स परिचित लगेगा:

myArray+=( "newElement1" "newElement2" )

पैरामीटर स्वीप

सब कुछ एक साथ रखते हुए, यहाँ हमारे पैरामीटर स्वीप को लॉन्च करने के लिए हमारी स्क्रिप्ट है:

allThreads=(1 2 4 8 16 32 64 128)
allRuntimes=()
for t in ${allThreads[@]}; do
runtime=$(./pipeline --threads $t)
allRuntimes+=( $runtime )
done

और वोइला!

और क्या है आपके पास?

इस लेख में, हमने पैरामीटर स्वीप्स के लिए arrays के उपयोग का परिदृश्य कवर किया। लेकिन मैं वादा करता हूं कि Bash arrays का उपयोग करने के और भी कारण हैं—यहाँ दो और उदाहरण हैं।

लॉग अलर्टिंग

इस परिदृश्य में, आपका ऐप मॉड्यूल्स में विभाजित है, प्रत्येक का अपना लॉग फ़ाइल है। हम एक क्रॉन जॉब स्क्रिप्ट लिख सकते हैं जब कुछ मॉड्यूल्स में परेशानी के संकेत हों तो सही व्यक्ति को ईमेल करें:

# लॉग्स की सूची और किन्हें मुद्दों की सूचना दी जानी चाहिए
logPaths=("api.log" "auth.log" "jenkins.log" "data.log")
logEmails=("jay@email" "emma@email" "jon@email" "sophia@email")

# प्रत्येक लॉग में परेशानी के संकेत देखें
for i in ${!logPaths[@]};
do
log=${logPaths[$i]}
stakeholder=${logEmails[$i]}
numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l )

# स्टेकहोल्डर्स को चेतावनी दें यदि हाल ही में > 5 त्रुटियां देखी गईं
if [[ "$numErrors" -gt 5 ]];
then
emailRecipient="$stakeholder"
emailSubject="WARNING: ${log} showing unusual levels of errors"
emailBody="${numErrors} errors found in log ${log}"
echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient"
fi
done

API क्वेरीज

मान लीजिए आप अपने Medium पोस्ट्स पर सबसे अधिक कमेंट करने वाले यूजर्स के बारे में कुछ एनालिटिक्स जनरेट करना चाहते हैं। चूंकि हमारे पास डायरेक्ट डेटाबेस एक्सेस नहीं है, SQL सवाल से बाहर है, लेकिन हम APIs का उपयोग कर सकते हैं!

API ऑथेंटिकेशन और टोकन्स के बारे में लंबी चर्चा में पड़ने से बचने के लिए, हम इसके बजाय JSONPlaceholder, एक सार्वजनिक-सामना करने वाली API टेस्टिंग सेवा, को अपने एंडपॉइंट के रूप में उपयोग करेंगे। एक बार जब हम प्रत्येक पोस्ट को क्वेरी करते हैं और कमेंट करने वाले सभी के ईमेल पुनः प्राप्त करते हैं, तो हम उन ईमेल्स को अपने परिणाम array में जोड़ सकते हैं:

endpoint="https://jsonplaceholder.typicode.com/comments"
allEmails=()

# पहले 10 पोस्ट्स क्वेरी करें
for postId in {1..10};
do
# इस पोस्ट के कमेंटर्स के ईमेल लाने के लिए API कॉल करें
response=$(curl "${endpoint}?postId=${postId}")

# JSON रिस्पॉन्स को array में पार्स करने के लिए jq का उपयोग करें
allEmails+=( $( jq '.[].email' <<< "$response" ) )
done

ध्यान दें कि यहाँ मैं कमांड लाइन से JSON पार्स करने के लिए jq टूल का उपयोग कर रहा हूं। jq का सिंटैक्स इस लेख के दायरे से बाहर है, लेकिन मैं इसकी अत्यधिक अनुशंसा करता हूं कि आप इसमें देखें।

जैसा कि आप कल्पना कर सकते हैं, ऐसे अनगिनत अन्य परिदृश्य हैं जिनमें Bash arrays का उपयोग मदद कर सकता है, और मुझे आशा है कि इस लेख में उल्लिखित उदाहरणों ने आपको कुछ सोचने के लिए दिया है। यदि आपके अपने काम से साझा करने के लिए अन्य उदाहरण हैं, तो कृपया नीचे टिप्पणी छोड़ें।

लेकिन रुकिए, और भी है!

चूंकि हमने इस लेख में काफी array सिंटैक्स कवर किया है, यहाँ हमने क्या कवर किया है इसका सारांश है, साथ ही कुछ और उन्नत ट्रिक्स जो हमने कवर नहीं कीं:

सिंटैक्स परिणाम
arr=() एक खाली array बनाएं
arr=(1 2 3) array इनिशियलाइज़ करें
${arr[2]} तीसरा तत्व पुनः प्राप्त करें
${arr[@]} सभी तत्व पुनः प्राप्त करें
${!arr[@]} array इंडेक्सेस पुनः प्राप्त करें
${#arr[@]} array आकार की गणना करें
arr[0]=3 पहला तत्व ओवरराइट करें
arr+=(4) मान जोड़ें
str=$(ls) ls आउटपुट को स्ट्रिंग के रूप में सेव करें
arr=( $(ls) ) ls आउटपुट को फ़ाइलों के array के रूप में सेव करें
${arr[@]:s:n} इंडेक्स s से शुरू होने वाले n तत्व पुनः प्राप्त करें

एक अंतिम विचार

जैसा कि हमने खोजा है, Bash arrays का निश्चित रूप से अजीब सिंटैक्स है, लेकिन मुझे आशा है कि इस लेख ने आपको आश्वस्त किया है कि वे अत्यधिक शक्तिशाली हैं। एक बार जब आप सिंटैक्स का आदी हो जाते हैं, तो आप खुद को Bash arrays का काफी बार उपयोग करते हुए पाएंगे।

Bash या Python?

जिससे सवाल उठता है: आपको अन्य स्क्रिप्टिंग भाषाओं जैसे Python के बजाय Bash arrays का उपयोग कब करना चाहिए?

मेरे लिए, यह सब निर्भरता पर उबलता है—यदि आप हाथ में समस्या को केवल कमांड-लाइन टूल्स के कॉल्स का उपयोग करके हल कर सकते हैं, तो आप भी Bash का उपयोग कर सकते हैं। लेकिन उन समयों के लिए जब आपकी स्क्रिप्ट एक बड़े Python प्रोजेक्ट का हिस्सा है, तो आप भी Python का उपयोग कर सकते हैं।

उदाहरण के लिए, हम पैरामीटर स्वीप को लागू करने के लिए Python की ओर रुख कर सकते थे, लेकिन हम अंततः Bash के चारों ओर सिर्फ एक रैपर लिख रहे होते:

import subprocess

all_threads = [1, 2, 4, 8, 16, 32, 64, 128]
all_runtimes = []

# प्रत्येक थ्रेड संख्या पर पाइपलाइन लॉन्च करें
for t in all_threads:
cmd = './pipeline --threads {}'.format(t)

# रिटर्न आउटपुट लाने के लिए subprocess मॉड्यूल का उपयोग करें
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]
all_runtimes.append(output)

चूंकि इस उदाहरण में कमांड लाइन से बचने का कोई रास्ता नहीं है, सीधे Bash का उपयोग करना बेहतर है।

बेशर्म प्लग का समय

यह लेख OSCON पर मेरे द्वारा दिए गए एक टॉक पर आधारित है, जहां मैंने लाइव-कोडिंग वर्कशॉप You Don't Know Bash प्रस्तुत की। कोई स्लाइड्स नहीं, कोई क्लिकर्स नहीं—बस मैं और दर्शक कमांड लाइन पर टाइप कर रहे हैं, Bash की अद्भुत दुनिया की खोज कर रहे हैं।

  1. एंड्रॉइड 10: वह सब जो आपको जानना चाहिए - फीचर्स, डार्क मोड, और अपग्रेड गाइड एंड्रॉइड 10: वह सब जो आपको जानना चाहिए - फीचर्स, डार्क मोड, और अपग्रेड गाइड

    एंड्रॉइड ने आधिकारिक तौर पर अपना नवीनतम वर्जन एंड्रॉइड 10 जारी किया है, जो अब Google Pixel फोन पर इंस्टॉल करने के लिए उपलब्ध है। इस अपडेट के साथ, अब एंड्रॉइड उपयोगकर्ता डार्क थीम का लाभ उठा सकते हैं। रिलीज से कुछ दिन पहले, यह अफवाह थी कि Google आखिरकार डेज़र्ट-विशिष्ट नाम की एकरसता को तोड़ रहा है, ज

  1. आपके USB ड्राइव के 5 छिपे हुए उपयोग आपके USB ड्राइव के 5 छिपे हुए उपयोग

    एक PC से दूसरे PC में डेटा ट्रांसपोर्ट करने के लिए सबसे पहले जो डिवाइस हमारे दिमाग में आता है वह है USB या पेन ड्राइव। निस्संदेह आज के युग में स्टोरेज मीडिया ने नई ऊंचाइयों को छुआ है फिर भी हम में से कई अभी भी USB का उपयोग न केवल डेटा ट्रांसफर करने के लिए बल्कि इसे सेव करने के लिए भी करते हैं। लेकिन

  1. Wi-Fi 6 समझाया गया: प्रमुख विशेषताएं, लाभ और यह कनेक्टिविटी को कैसे बदलता है Wi-Fi 6 समझाया गया: प्रमुख विशेषताएं, लाभ और यह कनेक्टिविटी को कैसे बदलता है

    इस साल कई टेक सरप्राइज इंतजार कर रहे हैं! और Wi-Fi 6 उनमें से एक है। हां, सही है। अगली पीढ़ी के Wi-Fi के लिए तैयार हो जाइए जो 2019 में हम देखेंगे एक गेम-चेंजिंग वायरलेस मानक। चूंकि हम टेक्नोलॉजी और गैजेट्स के प्रति अधिक प्रवृत्त हो रहे हैं, Wi-Fi 6 का आगमन निश्चित रूप से एक अच्छी खबर है। यह विशेष रू