मैं बैश में एक सीमांकक पर एक स्ट्रिंग कैसे विभाजित करूं?


2039

मेरे पास यह स्ट्रिंग एक चर में संग्रहीत है:

IN="bla@some.com;john@home.com"

अब मैं ;परिसीमन द्वारा तार को विभाजित करना चाहूंगा ताकि मेरे पास:

ADDR1="bla@some.com"
ADDR2="john@home.com"

मैं ADDR1और ADDR2चर की जरूरत नहीं है । अगर वे किसी ऐसे ऐरे के तत्व हैं जो और भी बेहतर है।


नीचे दिए गए उत्तरों के सुझावों के बाद, मैंने निम्नलिखित को समाप्त किया, जो कि मैं उसके बाद था:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

आउटपुट:

> [bla@some.com]
> [john@home.com]

एक समाधान था जिसमें Internal_field_separator (IFS) को सेट करना शामिल था ;। मुझे यकीन नहीं है कि उस उत्तर के साथ क्या हुआ, आप IFSडिफ़ॉल्ट पर वापस कैसे रीसेट करते हैं ?

पुन: IFSसमाधान, मैंने यह कोशिश की और यह काम करता है, मैं पुराना रखता हूं IFSऔर फिर इसे पुनर्स्थापित करता हूं :

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

BTW, जब मैंने कोशिश की

mails2=($IN)

मुझे केवल पहला स्ट्रिंग मिला है जब इसे लूप में प्रिंट करते हैं, $INइसके चारों ओर ब्रैकेट्स के बिना काम करता है।


14
अपने "एडिट 2" के संबंध में: आप बस "IFS को परेशान" कर सकते हैं और यह डिफ़ॉल्ट स्थिति में वापस आ जाएगा। इसे स्पष्ट रूप से सहेजने और पुनर्स्थापित करने की कोई आवश्यकता नहीं है जब तक कि आपके पास यह अपेक्षा करने का कोई कारण नहीं है कि यह पहले से ही एक गैर-डिफ़ॉल्ट मान पर सेट है। इसके अलावा, यदि आप किसी फ़ंक्शन के अंदर ऐसा कर रहे हैं (और, यदि आप ऐसा नहीं कर रहे हैं, तो क्यों नहीं?), तो आप IFS को एक स्थानीय चर के रूप में सेट कर सकते हैं और फ़ंक्शन से बाहर निकलते ही यह अपने पिछले मूल्य पर लौट आएगा।
ब्रूक्स मूसा

19
@BrooksMoses: (ए) local IFS=...जहां संभव हो उपयोग करने के लिए +1 ; (बी) -1 के लिए unset IFS, यह आईएफएस को उसके डिफ़ॉल्ट मान पर रीसेट नहीं करता है, हालांकि मेरा मानना ​​है कि एक परेशान आईएफएस आईएफएस ($ '\ t \ n') के डिफ़ॉल्ट मान के समान व्यवहार करता है, हालांकि यह बुरा अभ्यास लगता है आँख बंद करके यह मान लेना कि आपका कोड कभी भी IFS के साथ कस्टम मूल्य पर सेट नहीं किया जाएगा; (ग) एक अन्य विचार एक उपधारा को लागू करने के लिए है: (IFS=$custom; ...)जब उपधारा IFS से बाहर निकलता है तो यह मूल रूप से जो कुछ भी है, वापस आ जाएगा।
डब्यूजिम

मैं सिर्फ यह देखना चाहता हूं कि एक निष्पादन योग्य को फेंकने के लिए रास्तों पर एक त्वरित नजर है, इसलिए मैंने दौड़ने का सहारा लिया ruby -e "puts ENV.fetch('PATH').split(':')"। यदि आप शुद्ध रहना चाहते हैं तो मदद नहीं करेगा लेकिन किसी भी स्क्रिप्टिंग भाषा का उपयोग करना जिसमें अंतर्निहित विभाजन आसान है।
निकोगा

4
for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
user2037659

2
इसे एक सरणी के रूप में सहेजने के लिए मुझे कोष्ठक का एक और सेट रखना पड़ा और \nकेवल एक स्थान के लिए बदलना पड़ा । तो अंतिम पंक्ति है mails=($(echo $IN | tr ";" " "))। तो अब मैं के तत्वों की जांच कर सकते mailsसरणी संकेतन का उपयोग करके mails[index]या सिर्फ एक पाश में पुनरावृत्ति
afranques

जवाबों:


1232

आप आंतरिक क्षेत्र विभाजक (IFS) चर सेट कर सकते हैं , और फिर इसे एक सरणी में पार्स कर सकते हैं। जब यह एक कमांड में होता है, तो असाइनमेंट IFSकेवल उस एकल कमांड के वातावरण (से read) तक होता है। यह एक IFSचर में चर मूल्य के अनुसार इनपुट को पार्स करता है , जिसे हम बाद में पुनरावृत्त कर सकते हैं।

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

यह ;एक सरणी में धकेलते हुए , द्वारा अलग की गई वस्तुओं की एक पंक्ति को पार्स कर देगा । पूरी प्रक्रिया के लिए सामग्री $IN, हर बार इनपुट की एक पंक्ति को अलग करके ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"

22
यह शायद सबसे अच्छा तरीका है। IFS वर्तमान मूल्य में कब तक बना रहेगा, क्या यह सेट होने पर मेरे कोड को गड़बड़ कर सकता है और ऐसा नहीं होना चाहिए, और जब मैं इसके साथ काम करता हूं तो मैं इसे कैसे रीसेट कर सकता हूं?
क्रिस लूत्ज़

7
अब ठीक होने के बाद, केवल पढ़ी गई कमांड की अवधि के भीतर :)
जोहान्स स्काउब -

14
आप बिना लूप का उपयोग किए बिना एक बार में सब कुछ पढ़ सकते हैं: -r -d '-a' -a addr <<< "$" में "# -d '' यहाँ महत्वपूर्ण है, यह पहले न्यूलाइन पर रुकने के लिए रीड बताता है ( जो डिफ़ॉल्ट -d है) लेकिन EOF या NULL बाइट तक जारी रखने के लिए (जो केवल बाइनरी डेटा में होता है)।
लुननाथ

55
@LucaBorrione बिना किसी अर्धविराम या अन्य विभाजक IFSके readसाथ एक ही पंक्ति पर सेटिंग करना , जैसा कि एक अलग कमांड में विरोध किया गया है, यह उस कमांड को स्कोप करता है - इसलिए यह हमेशा "बहाल" होता है; आपको मैन्युअल रूप से कुछ भी करने की आवश्यकता नहीं है।
चार्ल्स डफी

5
@imagineerThis IFS के लिए पहलवानों और स्थानीय परिवर्तनों से युक्त एक बग $INहै जिसे उद्धृत करने की आवश्यकता है। बग bash4.3 में तय किया गया है ।
शेपनर

971

बैश शैल स्क्रिप्ट विभाजन सरणी से लिया गया :

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })

स्पष्टीकरण:

यह निर्माण स्ट्रिंग में (एकल स्थान) के सभी आवृत्तियों ';'( एकल स्थान) के स्थान पर //प्रतिस्थापित करता है , फिर एक स्थान के रूप में अंतरिक्ष-सीमांकित स्ट्रिंग की व्याख्या करता है (यही वह है जो आसपास के कोष्ठक करते हैं)।IN' '

प्रत्येक ';'वर्ण को वर्ण के साथ बदलने के लिए घुंघराले ब्रेसिज़ के अंदर उपयोग किए जाने वाले वाक्यविन्यास ' 'को पैरामीटर विस्तार कहा जाता है ।

कुछ सामान्य गोत्र हैं:

  1. यदि मूल स्ट्रिंग में स्थान हैं, तो आपको IFS का उपयोग करना होगा :
    • IFS=':'; arrIN=($IN); unset IFS;
  2. यदि मूल स्ट्रिंग में स्थान है और सीमांकक एक नई पंक्ति है, तो आप IFS को इसके साथ सेट कर सकते हैं :
    • IFS=$'\n'; arrIN=($IN); unset IFS;

84
मैं बस जोड़ना चाहता हूं: यह सबसे सरल है, आप सरणी तत्वों को $ {arrin [1]} (पाठ्यक्रम के शून्य से शुरू) तक
पहुंच सकते हैं

26
इसे पाया: $ {} के भीतर एक चर को संशोधित करने की तकनीक को 'पैरामीटर विस्तार' के रूप में जाना जाता है।
KomodoDave

22
नहीं, मुझे नहीं लगता कि यह तब काम करता है जब वहाँ भी रिक्त स्थान मौजूद हों ... यह ',' को '' में परिवर्तित कर रहा है और फिर एक अंतरिक्ष-पृथक सरणी का निर्माण कर रहा है।
ईथन

12
बहुत संक्षिप्त है, लेकिन सामान्य उपयोग के लिए चेतावनी हैं : शेल स्ट्रिंग में शब्द विभाजन और विस्तार को लागू करता है, जिसे अनदेखा किया जा सकता है; बस इसके साथ प्रयास करें। IN="bla@some.com;john@home.com;*;broken apart"। संक्षेप में: यह दृष्टिकोण टूट जाएगा, अगर आपके टोकन में एम्बेडेड स्थान और / या चार्ट हैं। जैसे *कि वर्तमान फ़ोल्डर में टोकन मिलान फ़ाइलनाम बनाने के लिए होता है।
mklement0

53
यह अन्य कारणों से एक बुरा दृष्टिकोण है: उदाहरण के लिए, यदि आपकी स्ट्रिंग में है ;*;, तो *वर्तमान निर्देशिका में फ़ाइल नाम की सूची में विस्तार किया जाएगा। -1
चार्ल्स डफी

249

यदि आप उन्हें तुरंत संसाधित करने से इनकार नहीं करते हैं, तो मुझे यह करना पसंद है:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

आप किसी सरणी को आरंभीकृत करने के लिए इस तरह के लूप का उपयोग कर सकते हैं, लेकिन ऐसा करने का एक आसान तरीका है। आशा है कि यह मदद करता है, यद्यपि।


आपको IFS उत्तर रखना चाहिए था। इसने मुझे कुछ सिखाया जो मैं नहीं जानता था, और इसने निश्चित रूप से एक सरणी बनाई, जबकि यह सिर्फ एक सस्ता विकल्प बनाता है।
क्रिस लुट्ज़

समझा। हाँ, मैं इन मूर्खतापूर्ण प्रयोगों को कर रहा हूं, मैं हर बार नई चीजें सीखने जा रहा हूं, मैं चीजों का जवाब देने की कोशिश कर रहा हूं। मैंने #bash IRC फ़ीडबैक और अनिर्धारित के आधार पर सामान संपादित किया है :)
-

33
-1, आपको स्पष्ट रूप से शब्दों के बारे में पता नहीं है, क्योंकि यह आपके कोड में दो बग पेश कर रहा है। एक वह है जब आप $ IN को उद्धृत नहीं करते हैं और दूसरा यह है कि जब आप एक नई पंक्ति का दिखावा करते हैं तो केवल शब्दों के उपयोग में परिसीमन होता है। आप प्रत्येक WORD में IN, प्रत्येक पंक्ति पर नहीं देख रहे हैं, और निश्चित रूप से प्रत्येक तत्व अर्धविराम द्वारा सीमांकित नहीं है, हालांकि यह काम करने की तरह दिखने का दुष्प्रभाव हो सकता है।
प्रात:

3
आप इसे "$ IN" प्रतिध्वनि में बदल सकते हैं tr '?' '\ n' | जबकि पढ़ें -r ADDY; # प्रक्रिया "$ ADDY" करें; उसे भाग्यशाली बनाने के लिए किया गया, मुझे लगता है :) ध्यान दें कि यह कांटा होगा, और आप लूप के भीतर से बाहरी चर नहीं बदल सकते हैं (यही कारण है कि मैंने <<< "$" को "सिंटैक्स" में इस्तेमाल किया है)
जोहान्स स्काउब - लिट

8
टिप्पणियों में बहस को संक्षेप में प्रस्तुत करने के लिए : सामान्य उपयोग के लिए कैविट्स : शेल शब्द विभाजन और स्ट्रिंग के विस्तार पर लागू होता है , जो अवांछित हो सकता है; बस इसके साथ प्रयास करें। IN="bla@some.com;john@home.com;*;broken apart"। संक्षेप में: यह दृष्टिकोण टूट जाएगा, अगर आपके टोकन में एम्बेडेड स्थान और / या चार्ट हैं। जैसे *कि वर्तमान फ़ोल्डर में टोकन मिलान फ़ाइलनाम बनाने के लिए होता है।
mklement0

202

संगत जवाब

ऐसा करने के लिए बहुत सारे अलग-अलग तरीके हैं

हालाँकि, पहले यह ध्यान रखना महत्वपूर्ण है कि 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>]

मज़े करो!


15
#, ##, %, और %%प्रतिस्थापन क्या IMO याद करने के लिए एक आसान विवरण (वे कितना हटाना के लिए) है: #और %कम से कम से मिलता जुलता स्ट्रिंग हटा देते हैं और ##और %%सबसे लंबे समय तक संभव हटा दें।
स्कोर_उंडर

1
IFS=\; read -a fields <<<"$var"नई-पंक्तियों पर विफल रहता है और उनके पीछे न्यू लाइन जोड़ने। अन्य समाधान एक अनुगामी खाली क्षेत्र को निकालता है।
इसहाक

शेल सीमांकक सबसे सुरुचिपूर्ण उत्तर है, अवधि।
एरिक चेन

क्या अंतिम विकल्प का उपयोग क्षेत्र विभाजकों की सूची के साथ कहीं और किया जा सकता है? उदाहरण के लिए, मेरा मतलब इसे शेल स्क्रिप्ट के रूप में उपयोग करना है, और एक स्थिति पैरामीटर के रूप में क्षेत्र विभाजकों की सूची पास करना है।
sancho.s ReinstateMonicaCellio

हां, एक लूप में:for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
एफ। होरी

183

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

इस विशिष्ट उदाहरण को बैश स्क्रिप्ट ऐरे में विभाजित करने के मामले में, trसंभवतः अधिक कुशल है, लेकिन cutइसका उपयोग किया जा सकता है, और यदि आप बीच से विशिष्ट क्षेत्रों को खींचना चाहते हैं तो यह अधिक प्रभावी है।

उदाहरण:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

आप स्पष्ट रूप से एक लूप में डाल सकते हैं, और प्रत्येक क्षेत्र को स्वतंत्र रूप से खींचने के लिए -f पैरामीटर का उपयोग करते हैं।

यह तब अधिक उपयोगी होता है जब आपके पास पंक्तियों के साथ एक सीमांकित लॉग फ़ाइल होती है:

2015-04-27|12345|some action|an attribute|meta data

cutcatइस फ़ाइल में सक्षम होना बहुत आसान है और आगे की प्रक्रिया के लिए एक विशेष क्षेत्र का चयन करना है।


6
का उपयोग करने के लिए यश cut, यह काम के लिए सही उपकरण है! उन शेल हैक्स की तुलना में बहुत कुछ साफ हो गया है।
मिस्टरमियागी

4
यह दृष्टिकोण केवल तभी काम करेगा जब आप पहले से तत्वों की संख्या जानते हैं; आपको इसके आसपास कुछ और तर्क देने की आवश्यकता होगी। यह हर तत्व के लिए एक बाहरी उपकरण भी चलाता है।
४२४२

आमतौर पर मैं एक सीएसवी में खाली स्ट्रिंग से बचने की कोशिश कर रहा था। अब मैं सटीक 'कॉलम' मान को भी इंगित कर सकता हूं। IFS के साथ काम पहले से ही एक लूप में उपयोग किया जाता है। मेरी स्थिति के लिए उम्मीद से बेहतर।
लुई लाउडोग ट्रॉटियर

आईडी और पीआईडी ​​भी खींचने के लिए बहुत उपयोगी है
मिलोस ग्रुजिक

यह उत्तर आधे पृष्ठ से नीचे स्क्रॉल करने लायक है :)
Gucu112

124

यह मेरे लिए काम किया:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

1
हालांकि यह केवल एक ही चरित्र परिसीमन के साथ काम करता है, लेकिन यह वही है जो ओपी खोज रहा था (एक अर्धविराम द्वारा सीमांकित रिकॉर्ड)।
ग्यूपैडपॉक

चार साल पहले के बारे में @Ashok द्वारा उत्तर दिया गया था , और भी, एक वर्ष से अधिक पहले @DougW द्वारा , आपके उत्तर की तुलना में, और भी अधिक जानकारी के साथ। कृपया दूसरों से अलग समाधान पोस्ट करें '।
मेकित्गरहा

90

इस दृष्टिकोण के बारे में कैसे:

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

स्रोत


7
+1 ... लेकिन मैं "ऐरे" चर का नाम नहीं लूंगा ... पालतू जीव मुझे लगता है। अच्छा समाधान।
यज़्मीर रामिरेज़

14
+1 ... लेकिन "सेट" और घोषणा-ए अनावश्यक हैं। आप के रूप में अच्छी तरह से बस इस्तेमाल कर सकते हैंIFS";" && Array=($IN)
ata

+1 केवल एक ओर ध्यान दें: क्या पुराने IFS को रखने और फिर इसे बहाल करने की सिफारिश नहीं की जानी चाहिए? (जैसा कि उनके संपादन 3 में stefanB द्वारा दिखाया गया है) यहां उतरने वाले लोग (कभी-कभी केवल एक प्रतिलिपि और समाधान को चिपकाते हैं) इस बारे में नहीं सोच सकते हैं
Luca Borrione

6
-1: सबसे पहले, @ata सही है कि इसमें अधिकांश कमांड कुछ नहीं करते हैं। दूसरा, यह सरणी बनाने के लिए शब्द-विभाजन का उपयोग करता है, और ऐसा करते समय ग्लोब-विस्तार को बाधित करने के लिए कुछ भी नहीं करता है (इसलिए यदि आपके पास किसी भी सरणी तत्वों में ग्लोब वर्ण हैं, तो उन तत्वों को मिलान फ़ाइल नाम के साथ बदल दिया जाता है)।
चार्ल्स डफी

1
उपयोग करने के लिए सुझाव $'...': IN=$'bla@some.com;john@home.com;bet <d@\ns* kl.com>'। फिर echo "${Array[2]}"न्यूलाइन के साथ एक स्ट्रिंग प्रिंट करेगा। set -- "$IN"इस मामले में भी neccessary है। हां, ग्लोब विस्तार को रोकने के लिए, समाधान में शामिल होना चाहिए set -f
जॉन_वेस्ट John

79

मुझे लगता है कि आपकी समस्या को हल करने के लिए AWK सबसे अच्छा और कुशल कमांड है। AWK डिफ़ॉल्ट रूप से लगभग हर लिनक्स वितरण में शामिल है।

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

दे देंगे

bla@some.com john@home.com

बेशक, आपके प्रत्येक ईमेल पते को awk प्रिंट फ़ील्ड को फिर से परिभाषित करके स्टोर कर सकते हैं।


3
या इससे भी सरल: इको "bla@some.com; john@home.com" | awk 'BEGIN {RS = ";"} {प्रिंट}'
Jaro

@ जारो यह मेरे लिए पूरी तरह से काम करता है जब मुझे कॉमा के साथ एक स्ट्रिंग होती है और इसे लाइनों में सुधार करने की आवश्यकता होती है। धन्यवाद।
एक्वेरेल

इसने इस परिदृश्य में काम किया -> "इको" $ SPLIT_0 "| awk -F 'इनोड =' '{प्रिंट $ 1}" "! मुझे वर्णों के बजाय स्प्रिंग्स ("इनोड =") का उपयोग करने की कोशिश करते समय समस्याएं थीं (";")। $ 1, $ 2, $ 3, $ 4 को एक सरणी में पदों के रूप में सेट किया गया है! अगर एक सरणी सेट करने का एक तरीका है ... बेहतर! धन्यवाद!
एडुआर्डो लुसियो

@EduardoLucio, मैं जिसके बारे में सोच रहा हूं, हो सकता है कि आप पहले अपने सीमांकक inode=को ;उदाहरण के लिए बदल सकते हैं sed -i 's/inode\=/\;/g' your_file_to_process, फिर -F';'लागू करते समय परिभाषित करें awk, आशा है कि आपकी मदद कर सकता है।
टोंग

66
echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

4
-1 क्या होगा यदि स्ट्रिंग में रिक्त स्थान हैं? उदाहरण के लिए IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )इस मामले में 8 तत्वों की एक सरणी का उत्पादन होगा (प्रत्येक शब्द अंतरिक्ष के लिए एक तत्व अलग), बजाय 2 (प्रत्येक पंक्ति अर्ध
कॉलोन के

3
@ लुका नहीं, सेड स्क्रिप्ट बिल्कुल दो लाइनें नहीं बनाती है। आपके लिए कई प्रविष्टियाँ बनाता है जब आप इसे एक बैश सरणी में डालते हैं (जो डिफ़ॉल्ट रूप से सफेद स्थान पर विभाजित होता है)
लोथार

ठीक यही बिंदु है: ओपी को प्रविष्टियों को लूप में स्टोर करने की आवश्यकता होती है, जैसा कि आप उनके संपादन में देख सकते हैं। मुझे लगता है कि आपके (अच्छे) उत्तर arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )को प्राप्त करने के लिए उपयोग करने के लिए, और IFS=$'\n'भविष्य में यहां आने वाले लोगों के लिए IFS को बदलने की सलाह देने के लिए याद किया गया है और एक स्ट्रिंग युक्त रिक्त स्थान को विभाजित करने की आवश्यकता है। (और बाद में इसे वापस लाने के लिए)। :)
लुका बोर्रियन

1
@ लुका अच्छा बिंदु। हालाँकि सरणी असाइनमेंट उस प्रारंभिक प्रश्न में नहीं था जब मैंने उस उत्तर को लिखा था।
लोथर

65

यह भी काम करता है:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

सावधान रहें, यह समाधान हमेशा सही नहीं होता है। यदि आप केवल "bla@some.com" पास करते हैं, तो यह ADD1 और ADD2 दोनों को असाइन करेगा।


1
आप बताई गई समस्या से बचने के लिए -s का उपयोग कर सकते हैं: superuser.com/questions/896800/… ”-f, --fields = LIST केवल इन फ़ील्ड्स का चयन करें; साथ ही किसी भी लाइन को प्रिंट करें जिसमें कोई सीमांकक वर्ण न हो, जब तक -s विकल्प नहीं होता। निर्दिष्ट "
fersarr

34

डैरन के जवाब पर एक अलग तरह से , यह मैं कैसे करता हूं:

IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

मुझे लगता है कि यह करता है! ऊपर दिए गए आदेशों को चलाएं और फिर "$ $ ADDR1 ... $ ADDR2" और मुझे "bla@some.com ... john@home.com" आउटपुट मिलता है
nicjb

1
यह वास्तव में मेरे लिए अच्छी तरह से काम किया ... मैंने इसे स्ट्रिंग के एक सरणी पर पुनरावृत्त करने के लिए उपयोग किया, जिसमें अल्पविराम से अलग DB, SERVER, पोर्ट डेटा का उपयोग करके mysqldump का उपयोग किया गया था।
निक

5
निदान: IFS=";"असाइनमेंट केवल $(...; echo $IN)उपधारा में मौजूद है ; यही कारण है कि कुछ पाठक (मेरे सहित) शुरू में सोचते हैं कि यह काम नहीं करेगा। मुझे लगता है कि सभी $ IN ADDR1 द्वारा धीमा हो रहा था। लेकिन निकज सही है; यह वास्तव में कारगर है। कारण यह है कि echo $INकमांड $ IFS के वर्तमान मूल्य का उपयोग करके अपने तर्कों को पार्स करता है, लेकिन फिर $ IFS की स्थापना की परवाह किए बिना, एक अंतरिक्ष सीमांकक का उपयोग करके stdout करने के लिए उन्हें echoes। तो शुद्ध प्रभाव ऐसा है जैसे कि किसी ने फोन किया था read ADDR1 ADDR2 <<< "bla@some.com john@home.com"(ध्यान दें कि इनपुट अंतरिक्ष-पृथक नहीं है; -पारदर्शी)।
दुबिज्जीम

1
यह रिक्त स्थान और newlines पर विफल रहता है, और एक अनछुए चर विस्तार *के echo $INसाथ वाइल्डकार्ड का विस्तार भी करता है।
इसहाक

मुझे वास्तव में यह समाधान पसंद है। यह क्यों काम करता है का एक विवरण बहुत उपयोगी होगा और इसे बेहतर समग्र उत्तर देगा।
माइकल गास्किल

32

बैश में, एक बुलेट प्रूफ तरीका, जो आपके वैरिएबल में नई सीमाएँ होने पर भी काम करेगा:

IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

देखो:

$ in=$'one;two three;*;there is\na newline\nin this field'
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is
a newline
in this field")'

इसके लिए काम करने की चाल एक खाली सीमांकक के साथ (सीमांकक) के -dविकल्प का उपयोग करना readहै, ताकि readउसे खिलाया गया सब कुछ पढ़ने के लिए मजबूर किया जाए। और हम readचर की सामग्री के साथ फ़ीड करते हैं in, जिसमें कोई अनुगामी न्यूलाइन नहीं है printf। ध्यान दें कि हम यह भी printfसुनिश्चित करने के लिए सीमांकक डाल रहे हैं कि स्ट्रिंग readएक अनुगामी परिसीमन के पास है। इसके बिना, readखाली क्षेत्रों में संभावित अनुगामी छंटनी होगी:

$ in='one;two;three;'    # there's an empty field
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'

अनुगामी खाली क्षेत्र संरक्षित है।


Bash≥4.4 के लिए अद्यतन करें

बश 4.4 के बाद से, बिलिन mapfile(उर्फ readarray) -dएक सीमांकक निर्दिष्ट करने के विकल्प का समर्थन करता है। इसलिए एक और विहित तरीका है:

mapfile -d ';' -t array < <(printf '%s;' "$in")

5
मैंने इसे उस सूची पर दुर्लभ समाधान के रूप में पाया जो सही ढंग से \n, रिक्त स्थान और *एक साथ काम करता है । इसके अलावा, कोई छोरों; सरणी चर निष्पादन के बाद शेल में पहुंच योग्य है (उच्चतम उत्कीर्ण उत्तर के विपरीत)। ध्यान दें, in=$'...'यह दोहरे उद्धरण चिह्नों के साथ काम नहीं करता है। मुझे लगता है, इसे और अधिक बढ़ाने की जरूरत है।
जॉन_वेस्ट '

28

कैसे इस एक लाइनर के बारे में, अगर आप सरणियों का उपयोग नहीं कर रहे हैं:

IFS=';' read ADDR1 ADDR2 <<<$IN

read -r ...यह सुनिश्चित करने के लिए उपयोग करने पर विचार करें कि, उदाहरण के लिए, इनपुट में दो अक्षर "\ t" आपके चर में एक ही दो वर्णों (एकल टैब चार के बजाय) के रूप में समाप्त होते हैं।
डब्यूजिम

-1 यह यहां काम नहीं कर रहा है (ubuntu 12.04)। echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"अपने स्निपेट में जोड़ने से आउटपुट ADDR1 bla@some.com john@home.com\nADDR2(\ n न्यूलाइन है)
लुका बोर्रोन

यह संभवत: इसमें शामिल बग के कारण है IFSऔर यहां के तार bash4.3 में तय किए गए थे । उद्धृत करते हुए $INइसे ठीक करना चाहिए। (सिद्धांत में, $INशब्द के विस्तार या ग्लोबिंग के अधीन नहीं है क्योंकि यह फैलता है, जिसका अर्थ है कि उद्धरण अनावश्यक होना चाहिए। 4.3 में भी, हालांकि, कम से कम एक बग शेष है - रिपोर्ट और निर्धारित होने के लिए निर्धारित है - इसलिए उद्धरण एक अच्छा बना हुआ है विचार।)
चेपनर

यह टूट जाता है अगर $ में नई सुर्खियां हों तो भी $ IN को उद्धृत किया जाता है। और एक अनुगामी न्यूलाइन जोड़ता है।
इसहाक

इस के साथ एक समस्या, और कई अन्य समाधान यह भी है कि यह मानता है कि $ IN में बिल्कुल दो तत्व हैं - या कि आप ADDR2 में दूसरे और बाद के आइटम एक साथ तोड़े जाने के लिए तैयार हैं। मैं समझता हूं कि यह पूछ से मिलता है, लेकिन यह एक टाइम बम है।
स्टीवन आसानी से उपयोग होने वाला

21

आईएफएस की स्थापना के बिना

यदि आपके पास सिर्फ एक बृहदान्त्र है तो आप ऐसा कर सकते हैं:

a="foo:bar"
b=${a%:*}
c=${a##*:}

तुम्हे मिल जाएगा:

b = foo
c = bar

20

यहाँ एक साफ 3-लाइनर है:

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

जहां IFSविभाजक के आधार पर शब्दों का परिसीमन ()किया जाता है और एक सरणी बनाने के लिए उपयोग किया जाता है । फिर [@]प्रत्येक आइटम को एक अलग शब्द के रूप में वापस करने के लिए उपयोग किया जाता है।

यदि आपके पास उसके बाद कोई कोड है, तो आपको भी पुनर्स्थापित करने की आवश्यकता है $IFS, जैसे unset IFS


5
$inअयोग्य के उपयोग से वाइल्डकार्ड का विस्तार करने की अनुमति मिलती है।
इसहाक

10

निम्नलिखित बैश / zsh फ़ंक्शन दूसरे तर्क द्वारा दिए गए सीमांकक पर अपना पहला तर्क विभाजित करता है:

split() {
    local string="$1"
    local delimiter="$2"
    if [ -n "$string" ]; then
        local part
        while read -d "$delimiter" part; do
            echo $part
        done <<< "$string"
        echo $part
    fi
}

उदाहरण के लिए, कमांड

$ split 'a;b;c' ';'

पैदावार

a
b
c

उदाहरण के लिए, यह आउटपुट अन्य कमांडों पर लगाया जा सकता है। उदाहरण:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

दिए गए अन्य समाधानों की तुलना में, इसके निम्नलिखित फायदे हैं:

  • IFSओवरराइड नहीं किया जाता है: यहां तक ​​कि स्थानीय चर के गतिशील स्कोपिंग के कारण, IFSएक लूप पर ओवरराइड करने से लूप के भीतर से किए गए फ़ंक्शन कॉल्स में लीक होने का नया मूल्य होता है।

  • Arrays का उपयोग नहीं किया जाता है: सरणी में स्ट्रिंग का उपयोग करके बैश और zsh में readध्वज की आवश्यकता होती है ।-a-A

यदि वांछित है, तो समारोह को स्क्रिप्ट में रखा जा सकता है:

#!/usr/bin/env bash

split() {
    # ...
}

split "$@"

1 वर्ण से अधिक के सीमांकक के साथ काम करना प्रतीत नहीं होता: विभाजन = $ (विभाजन "$ सामग्री" "फ़ाइल: //")
madprops

सच - से help read:-d delim continue until the first character of DELIM is read, rather than newline
हाले क्नस्ट

8

आप कई स्थितियों में awk को लागू कर सकते हैं

echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

यह भी आप इस का उपयोग कर सकते हैं

echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"

7

इस तरह एक सरल और स्मार्ट तरीका है:

echo "add:sfff" | xargs -d: -i  echo {}

लेकिन आपको gn xargs, BSD xargs cant support -d delim का उपयोग करना होगा। यदि आप मेरी तरह Apple मैक का उपयोग करते हैं। आप gnu xargs स्थापित कर सकते हैं:

brew install findutils

फिर

echo "add:sfff" | gxargs -d: -i  echo {}

4

यह इसे करने का सबसे सरल तरीका है।

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}

4

यहां कुछ शांत उत्तर हैं (इरेटा एस्प), लेकिन कुछ के लिए अन्य भाषाओं में विभाजित करने के लिए अनुरूप - जो कि मैंने मूल प्रश्न का अर्थ लिया है - मैं इस पर बस गया:

IN="bla@some.com;john@home.com"
declare -a a="(${IN/;/ })";

अब ${a[0]}, ${a[1]}आदि, जैसी आप उम्मीद करेंगे। ${#a[*]}शब्दों की संख्या के लिए उपयोग करें । या निश्चित रूप से,

for i in ${a[*]}; do echo $i; done

महत्वपूर्ण लेख:

यह उन मामलों में काम करता है जहां चिंता करने की कोई जगह नहीं है, जो मेरी समस्या को हल करता है, लेकिन आपका समाधान नहीं कर सकता है। $IFSउस मामले में समाधान के साथ जाओ ।


जब INदो से अधिक ई-मेल पते हों तो काम नहीं करता है । कृपया
पालिंड्रोम

${IN//;/ }इसे बनाने के लिए बेहतर उपयोग (डबल स्लैश) भी दो से अधिक मूल्यों के साथ काम करता है। खबरदार कि किसी भी वाइल्डकार्ड ( *?[) का विस्तार किया जाएगा। और एक अनुगामी खाली क्षेत्र त्याग दिया जाएगा।
इसाक

3
IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

उत्पादन

bla@some.com
john@home.com

सिस्टम: Ubuntu 12.04.1


IFS readयहां के विशिष्ट संदर्भ में सेट नहीं हो रहा है और इसलिए यह बाकी कोड को परेशान कर सकता है, यदि कोई हो।
कोडप्रोस्टर

2

यदि कोई स्थान नहीं है, तो यह क्यों नहीं?

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}

2

सरणी setको लोड करने के लिए अंतर्निहित का उपयोग करें $@:

IN="bla@some.com;john@home.com"
IFS=';'; set $IN; IFS=$' \t\n'

फिर, पार्टी शुरू करें:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2

set -- $INडैश के साथ शुरू होने वाले "$ IN" के साथ कुछ मुद्दों से बचने के लिए बेहतर उपयोग । फिर भी, अनियंत्रित विस्तार $INवाइल्डकार्ड ( *?[) का विस्तार करेगा ।
इसहाक

2

दो बोर्न-ईश विकल्प जहां न तो बैश सरण की आवश्यकता होती है:

केस 1 : इसे अच्छा और सरल रखें: एक न्यूलाइन का उपयोग रिकॉर्ड-सेपरेटर के रूप में करें ... जैसे।

IN="bla@some.com
john@home.com"

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

नोट: इस पहले मामले में कोई भी उप-प्रक्रिया सूची हेरफेर के साथ सहायता करने के लिए मनाई गई है।

विचार: शायद यह बड़े पैमाने पर NL का उपयोग कर के लायक है आंतरिक रूप से , और जब अंतिम परिणाम पैदा ही एक अलग रुपये में परिवर्तित करने के बाह्य

केस 2 : "?" का उपयोग करना एक रिकॉर्ड विभाजक के रूप में ... उदाहरण के लिए।

NL="
" IRS=";" ORS=";"

conv_IRS() {
  exec tr "$1" "$NL"
}

conv_ORS() {
  exec tr "$NL" "$1"
}

IN="bla@some.com;john@home.com"
IN="$(conv_IRS ";" <<< "$IN")"

while read i; do
  # process "$i" ... eg.
    echo -n "[email:$i]$ORS"
done <<< "$IN"

दोनों मामलों में लूप के पूरा होने के बाद लूप के भीतर एक सब-लिस्ट बनाई जा सकती है। यह उपयोगी है जब फ़ाइलों में सूचियों को संग्रहीत करने के बजाय मेमोरी में सूचियों में हेरफेर किया जाता है। {ps शांत रहें और B-) पर ले जाएं}


2

शानदार उत्तरों के अलावा जो पहले से ही प्रदान किए गए थे, अगर यह केवल उन आंकड़ों को प्रिंट करने की बात है, जिनका आप उपयोग करने पर विचार कर सकते हैं awk:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

यह फ़ील्ड विभाजक को सेट करता है ;, ताकि यह एक forलूप के साथ फ़ील्ड के माध्यम से लूप कर सके और तदनुसार प्रिंट कर सके।

परीक्षा

$ IN="bla@some.com;john@home.com"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [bla@some.com]
> [john@home.com]

एक अन्य इनपुट के साथ:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]

2

एंड्रॉइड शेल में, प्रस्तावित विधियों में से अधिकांश बस काम नहीं करते हैं:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

क्या काम करता है:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

जहां //वैश्विक प्रतिस्थापन का मतलब है।


1
$ PATH के किसी भी हिस्से में रिक्त स्थान (या newlines) शामिल हैं। साथ ही वाइल्डकार्ड (तारांकन *, प्रश्न चिह्न और ब्रेसिज़ […]) का विस्तार करता है।
इसहाक

2
IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f

आउटपुट:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

स्पष्टीकरण: कोष्ठक () का उपयोग करके सरल असाइनमेंट अर्धविराम से अलग की गई सूची को एक सरणी में परिवर्तित करता है बशर्ते कि आपने ऐसा करने के बाद सही IFS है। स्टैंडर्ड फॉर लूप उस सरणी में सामान्य रूप से अलग-अलग वस्तुओं को संभालता है। ध्यान दें कि IN वैरिएबल के लिए दी गई सूची को "हार्ड" उद्धृत किया जाना चाहिए, अर्थात एकल टिक के साथ।

IFS को बचाना और बहाल करना चाहिए क्योंकि बैश एक असाइनमेंट को उसी तरह नहीं मानता है जैसे कि कमांड। एक वैकल्पिक समाधान एक फ़ंक्शन के अंदर असाइनमेंट को लपेटने और संशोधित IFS के साथ उस फ़ंक्शन को कॉल करने के लिए है। उस स्थिति में IFS की अलग बचत / बहाली की जरूरत नहीं है। "Bize" को इंगित करने के लिए धन्यवाद।


!"#$%&/()[]{}*? are no problemअच्छा ... काफी नहीं: []*?ग्लोब कैरेक्टर हैं। तो इस निर्देशिका और फ़ाइल को बनाने के बारे में क्या: `mkdir '!" # $% &'; स्पर्श '? "# $% & / () [] {} आपको मिला हाहाहा - कोई समस्या नहीं है' और आपकी आज्ञा को चला रहा है? सरल सुंदर हो सकता है, लेकिन जब यह टूट गया है, तो यह टूट गया है।
gnourf_gniourf

@gniourf_gniourf स्ट्रिंग को एक चर में संग्रहीत किया जाता है। कृपया मूल प्रश्न देखें।
अंजस्केल

1
@ याजास्केल आपने मेरी टिप्पणी को पूरी तरह से नहीं समझा। एक खरोंच निर्देशिका में जाओ और इन आदेश जारी: mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'। वे केवल एक निर्देशिका और एक फ़ाइल बनाएंगे, अजीब दिखने वाले नामों के साथ, मुझे स्वीकार करना होगा। फिर सही के साथ अपने आदेशों को चलाने के INआप दिया IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'। आप देखेंगे कि आपको वह आउटपुट नहीं मिलेगा जिसकी आप अपेक्षा करते हैं। क्योंकि आप अपने स्ट्रिंग को विभाजित करने के लिए पथनाम विस्तार के अधीन एक विधि का उपयोग कर रहे हैं।
ग्नौरफ_गनीउरफ

यह प्रदर्शित करने के लिए है कि वर्ण है *, ?, [...]और यहां तक कि, अगर extglobसेट किया गया है, !(...), @(...), ?(...), +(...) कर रहे हैं इस विधि के साथ समस्याओं!
gnourf_gniourf

1
@gniourf_gniourf ग्लोबिंग पर विस्तृत टिप्पणियों के लिए धन्यवाद। मैंने ग्लोबिंग बंद करने के लिए कोड को समायोजित किया। मेरा कहना सिर्फ इतना था कि यह दिखाने के लिए कि साधारण कार्य विभाजन कार्य कर सकता है।
अंजस्केल

1

ठीक है दोस्तों!

यहाँ मेरा जवाब है!

DELIMITER_VAL='='

read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF

SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
   SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
    echo "$i"
done

यह दृष्टिकोण मेरे लिए "सर्वश्रेष्ठ" क्यों है?

दो कारणों से:

  1. आपको सीमांकक से बचने की आवश्यकता नहीं है;
  2. आपको खाली जगहों की समस्या नहीं होगी । मान सरणी में ठीक से अलग हो जाएगा!

[] के


FYI करें, /etc/os-releaseऔर /etc/lsb-releaseइसका मतलब खट्टा होना है, और पार्स नहीं किया गया है। तो आपका तरीका वाकई गलत है। इसके अलावा, आप एक सीमांकक पर एक स्ट्रिंग
ग्नौरफ_ग्निउरफ

0

'?' द्वारा अलग किए गए स्ट्रिंग को विभाजित करने के लिए एक-लाइनर एक सरणी में है:

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

यह केवल IFS को एक सब-सेट में सेट करता है, इसलिए आपको इसके मूल्य को बचाने और पुनर्स्थापित करने के बारे में चिंता करने की आवश्यकता नहीं है।


-1 यह यहां काम नहीं करता है (ubuntu 12.04)। इसमें सभी $ IN वैल्यू के साथ केवल पहला इको प्रिंट होता है, जबकि दूसरा खाली होता है। यदि आप इको "0:" $ {ADDRS [0]} \ n प्रतिध्वनि "1:" $ {ADDRS [1]} को आउटपुट 0: bla@some.com;john@home.com\n 1:(\ n नई लाइन है)
लुका बोरोयन

1
इस विचार stackoverflow.com/a/6583589/1032370 पर काम करने के विकल्प के लिए कृपया nicjb के उत्तर का संदर्भ लें
Luca Borrione

1
-1, 1. IFS को उस उप-समुच्चय में सेट नहीं किया जा रहा है (इसे "इको" के वातावरण में पारित किया जा रहा है, जो एक बिल्डिन है, इसलिए कुछ भी हो रहा है)। 2. $INउद्धृत किया गया है तो यह IFS विभाजन के अधीन नहीं है। 3. प्रक्रिया प्रतिस्थापन व्हॉट्सएप द्वारा विभाजित है, लेकिन यह मूल डेटा को दूषित कर सकता है।
स्कोर_उंडर

0

शायद सबसे सुरुचिपूर्ण समाधान नहीं है, लेकिन *रिक्त स्थान के साथ काम करता है :

IN="bla@so me.com;*;john@home.com"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

आउटपुट

> [bla@so me.com]
> [*]
> [john@home.com]

अन्य उदाहरण (शुरुआत और अंत में सीमांकक):

IN=";bla@so me.com;*;john@home.com;"
> []
> [bla@so me.com]
> [*]
> [john@home.com]
> []

मूल रूप से यह उदाहरण ;बनाने के अलावा हर चरित्र को हटा देता है delims;;;। तो फिर यह करता है forसे पाश 1के लिए number-of-delimitersके रूप में से गिना ${#delims}। अंतिम चरण सुरक्षित रूप से $iवें भाग का उपयोग करना है cut

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