संगत जवाब
ऐसा करने के लिए बहुत सारे अलग-अलग तरीके हैं दे घुमा के।
हालाँकि, पहले यह ध्यान रखना महत्वपूर्ण है कि bash
कई विशेष विशेषताएं (तथाकथित बशीज़ ) हैं जो किसी अन्य में काम नहीं करेंगीखोल।
विशेष रूप से, arrays , साहचर्य arrays , और पैटर्न प्रतिस्थापन , जो इस पोस्ट में समाधान के साथ-साथ थ्रेड में दूसरों के लिए उपयोग किया जाता है, बशीज़ हैं और अन्य गोले के तहत काम नहीं कर सकते हैं जो कई लोग उपयोग करते हैं।
उदाहरण के लिए: मेरे डेबियन जीएनयू / लिनक्स पर , एक मानक शेल कहा जाता हैपानी का छींटा; मैं ऐसे कई लोगों को जानता हूं जो एक और शेल का इस्तेमाल करना पसंद करते हैंक्ष; और एक विशेष उपकरण भी कहा जाता हैबिजीबॉक्स अपने स्वयं के दुभाषिया के साथ (एश)।
अनुरोध स्ट्रिंग
उपरोक्त प्रश्न में विभाजित होने वाली स्ट्रिंग है:
IN="bla@some.com;john@home.com"
मैं इस स्ट्रिंग के संशोधित संस्करण का उपयोग यह सुनिश्चित करने के लिए करूंगा कि मेरा समाधान व्हाट्सएप वाले तारों के लिए मजबूत है, जो अन्य समाधानों को तोड़ सकता है:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
में सीमांकक के आधार पर विभाजित स्ट्रिंग दे घुमा के (संस्करण> = ४.२)
में शुद्ध bash
, हम एक बना सकते हैं सरणी के लिए एक अस्थायी मूल्य द्वारा तत्वों विभाजन के साथ भारतीय विदेश सेवा ( इनपुट क्षेत्र विभाजक )। IFS, अन्य बातों के अलावा, किसी bash
वर्ण को परिभाषित करता है कि उसे किसी सरणी को परिभाषित करते समय तत्वों के बीच सीमांकक के रूप में व्यवहार करना चाहिए:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
# save original IFS value so we can restore it later
oIFS="$IFS"
IFS=";"
declare -a fields=($IN)
IFS="$oIFS"
unset oIFS
के नए संस्करणों में bash
, IFS परिभाषा के साथ एक कमांड को प्रीफ़िक्स करना, केवल उस कमांड के लिए IFS को बदलता है और तुरंत बाद के पिछले मान पर रीसेट करता है। इसका अर्थ है कि हम उपरोक्त को केवल एक पंक्ति में कर सकते हैं:
IFS=\; read -a fields <<<"$IN"
# after this command, the IFS resets back to its previous value (here, the default):
set | grep ^IFS=
# IFS=$' \t\n'
हम देख सकते हैं कि स्ट्रिंग IN
को एक सरणी में संग्रहीत किया गया है जिसका नाम fields
अर्धविराम पर विभाजित है:
set | grep ^fields=\\\|^IN=
# fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
# IN='bla@some.com;john@home.com;Full Name <fulnam@other.org>'
(हम इन चरों की सामग्रियों को भी प्रयोग करके प्रदर्शित कर सकते हैं declare -p
:)
declare -p IN fields
# declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
# declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
ध्यान दें कि विभाजन करने read
का सबसे तेज़ तरीका है क्योंकि वहाँ कोई कांटे या बाहरी संसाधन नहीं हैं ।
एक बार सरणी परिभाषित होने के बाद, आप प्रत्येक फ़ील्ड को संसाधित करने के लिए एक साधारण लूप का उपयोग कर सकते हैं (या, बल्कि, आपके द्वारा परिभाषित सरणी में प्रत्येक तत्व):
# `"${fields[@]}"` expands to return every element of `fields` array as a separate argument
for x in "${fields[@]}" ;do
echo "> [$x]"
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
या आप एक स्थानांतरण दृष्टिकोण का उपयोग करके प्रसंस्करण के बाद सरणी से प्रत्येक क्षेत्र को छोड़ सकते हैं , जो मुझे पसंद है:
while [ "$fields" ] ;do
echo "> [$fields]"
# slice the array
fields=("${fields[@]:1}")
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
और अगर आप सिर्फ एक सरल प्रिंटआउट चाहते हैं, तो आपको इसके ऊपर लूप करने की भी आवश्यकता नहीं है:
printf "> [%s]\n" "${fields[@]}"
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
अद्यतन: हाल ही में दे घुमा के > = 4.4
के नए संस्करणों में bash
, आप कमांड के साथ भी खेल सकते हैं mapfile
:
mapfile -td \; fields < <(printf "%s\0" "$IN")
यह वाक्यविन्यास विशेष वर्ण, नए अंक और खाली क्षेत्रों को संरक्षित करता है!
यदि आप खाली फ़ील्ड शामिल नहीं करना चाहते हैं, तो आप निम्न कार्य कर सकते हैं:
mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}") # drop '\n' added by '<<<'
इसके साथ mapfile
, आप एक सारणी घोषित करना भी छोड़ सकते हैं और सीमांकित तत्वों पर "लूप" को छोड़ सकते हैं, प्रत्येक पर एक फ़ंक्शन कॉल कर सकते हैं:
myPubliMail() {
printf "Seq: %6d: Sending mail to '%s'..." $1 "$2"
# mail -s "This is not a spam..." "$2" </path/to/body
printf "\e[3D, done.\n"
}
mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail
(ध्यान दें: \0
यदि आप स्ट्रिंग के अंत में खाली खेतों की परवाह नहीं करते हैं या वे मौजूद नहीं हैं तो प्रारूप स्ट्रिंग के अंत में बेकार है।)
mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail
# Seq: 0: Sending mail to 'bla@some.com', done.
# Seq: 1: Sending mail to 'john@home.com', done.
# Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
या आप उपयोग कर सकते हैं <<<
, और फ़ंक्शन बॉडी में यह जोड़ने वाली नई लाइन को छोड़ने के लिए कुछ प्रसंस्करण शामिल हैं:
myPubliMail() {
local seq=$1 dest="${2%$'\n'}"
printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest"
# mail -s "This is not a spam..." "$dest" </path/to/body
printf "\e[3D, done.\n"
}
mapfile <<<"$IN" -td \; -c 1 -C myPubliMail
# Renders the same output:
# Seq: 0: Sending mail to 'bla@some.com', done.
# Seq: 1: Sending mail to 'john@home.com', done.
# Seq: 2: Sending mail to 'Full Name <fulnam@other.org>', done.
में सीमांकक के आधार पर विभाजित स्ट्रिंग खोल
यदि आप उपयोग नहीं कर सकते हैं bash
, या यदि आप कुछ ऐसा लिखना चाहते हैं जिसका उपयोग कई अलग-अलग गोले में किया जा सकता है, तो आप अक्सर बशीज़ का उपयोग नहीं कर सकते हैं - और इसमें वे सरणियाँ शामिल हैं जिनका उपयोग हम ऊपर के समाधानों में कर रहे हैं।
हालांकि, हमें स्ट्रिंग के "तत्वों" पर लूप करने के लिए सरणियों का उपयोग करने की आवश्यकता नहीं है। एक पैटर्न के पहले या अंतिम घटना से एक स्ट्रिंग के सब्सट्रिंग को हटाने के लिए कई गोले में एक वाक्यविन्यास का उपयोग किया जाता है । ध्यान दें कि *
एक वाइल्डकार्ड है जो शून्य या अधिक वर्णों के लिए खड़ा है:
(अब तक पोस्ट किए गए किसी भी समाधान में इस दृष्टिकोण की कमी मुख्य कारण है जो मैं यह उत्तर लिख रहा हूं;)
${var#*SubStr} # drops substring from start of string up to first occurrence of `SubStr`
${var##*SubStr} # drops substring from start of string up to last occurrence of `SubStr`
${var%SubStr*} # drops substring from last occurrence of `SubStr` to end of string
${var%%SubStr*} # drops substring from first occurrence of `SubStr` to end of string
जैसा कि स्कोर_उंडर द्वारा समझाया गया है :
#
और क्रमशः स्ट्रिंग %
के प्रारंभ और अंत से सबसे कम संभव मिलान विकल्प को हटा दें , और
##
और %%
सबसे लंबे समय तक संभव मिलान विकल्प को हटा दें।
उपरोक्त सिंटैक्स का उपयोग करके, हम एक एप्रोच बना सकते हैं जहाँ हम स्ट्रिंगर से "एलीमेन्ट्स" को हटाते हैं और सीमांकक के ऊपर या उसके बाद सबस्ट्रिंग को हटाते हैं।
नीचे दिया गया कोडब्लॉक अच्छी तरह से काम करता है दे घुमा के(मैक ओएस सहित bash
),पानी का छींटा, क्ष, तथा बिजीबॉक्सकी एश:
IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$IN" ] ;do
# extract the substring from start of string up to delimiter.
# this is the first "element" of the string.
iter=${IN%%;*}
echo "> [$iter]"
# if there's only one element left, set `IN` to an empty string.
# this causes us to exit this `while` loop.
# else, we delete the first "element" of the string from IN, and move onto the next.
[ "$IN" = "$iter" ] && \
IN='' || \
IN="${IN#*;}"
done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]
मज़े करो!