क्या शेल में जावास्क्रिप्ट के "विभाजन ()" जैसा कुछ है?


18

split()किसी सरणी में स्ट्रिंग को तोड़ने के लिए जावास्क्रिप्ट में उपयोग करना बहुत आसान है ।

शेल स्क्रिप्ट के बारे में क्या?

कहो मैं यह करना चाहता हूं:

$ script.sh var1_var2_var3

जब उपयोगकर्ता ऐसे स्ट्रिंग var1_var2_var3को script.sh में देता है, तो स्क्रिप्ट के अंदर यह स्ट्रिंग को एक सरणी में बदल देगा

array=( var1 var2 var3 )
for name in ${array[@]}; do
    # some code
done

1
shellआप क्या कर रहे हैं, bashआप कर सकते हैंIFS='_' read -a array <<< "${string}"
gwillie

perlवह भी कर सकता है। यह "शुद्ध" शेल नहीं है, लेकिन यह काफी सामान्य है।
सोब्रीक

@ शोब्रिक मैं "शुद्ध" शेल की तकनीकी परिभाषा से भी अनजान हूं, लेकिन नोड है।
एमोरी

मैं 'डिफॉल्ट रूप से मेरे लिनक्स बॉक्स पर शायद स्थापित है' पर काम करना पसंद करता हूं और
माइनुटिया को फेटता

जवाबों:


24

बॉर्न / POSIX की तरह गोले एक विभाजन + ग्लोब ऑपरेटर है और यह हर बार जब आप एक पैरामीटर विस्तार छोड़ लागू है ( $var, $-...), आदेश प्रतिस्थापन ( $(...)), या गणित विस्तार ( $((...))) सूची संदर्भ में गैर उद्धृत।

वास्तव में, आपने गलती से इसे लागू कर दिया जब आपने इसके for name in ${array[@]}बजाय किया for name in "${array[@]}"। (वास्तव में, आपको उस ऑपरेटर को सावधान करना चाहिए जो गलती से उस ऑपरेटर को इस तरह आमंत्रित कर रहा है जो कई बग और सुरक्षा कमजोरियों का स्रोत है )।

यही कारण है कि ऑपरेटर के साथ कॉन्फ़िगर किया गया है $IFSविशेष पैरामीटर और (क्या हो पात्रों विभाजित (हालांकि कि अंतरिक्ष, टैब सावधान रहना है और एक विशेष उपचार वहाँ) प्राप्त न्यू लाइन को बताने के लिए) -fनिष्क्रिय करने का विकल्प ( set -f) या सक्षम ( set +f) globहिस्सा।

यह भी ध्यान रखें कि जब तक Sमें $IFSमूल रूप से किया गया था (बॉर्न शैल जहां में $IFSके लिए से आता है) SPOSIX गोले में, eparator, में पात्रों $IFSके बजाय के रूप में देखा जाना चाहिए सीमांकक या टर्मिनेटर्स (उदाहरण के लिए नीचे देखें)।

तो पर विभाजित करने के लिए _:

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
array=($string) # invoke the split+glob operator

for i in "${array[@]}"; do # loop over the array elements.

विभाजक और सीमांकक के बीच अंतर देखने के लिए , निम्न पर प्रयास करें:

string='var1_var2_'

वह इसे var1और var2केवल (कोई अतिरिक्त खाली तत्व नहीं) विभाजित करेगा ।

तो, इसे जावास्क्रिप्ट के समान बनाने के लिए split(), आपको एक अतिरिक्त कदम की आवश्यकता होगी:

string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
temp=${string}_ # add an extra delimiter
array=($temp) # invoke the split+glob operator

(ध्यान दें कि यह रिक्त $stringको 1 में विभाजित करेगा ( 0 नहीं ), जावास्क्रिप्ट की तरह split())।

विशेष उपचार टैब, स्पेस और न्यूलाइन प्राप्त करने के लिए, देखें:

IFS=' '; string=' var1  var2  '

(तुम कहाँ हो ) var1और के var2साथ

IFS='_'; string='_var1__var2__'

जहां आपको मिलता है: '', var1, '', var2, ''

ध्यान दें कि zshशेल उस स्प्लिट + ग्लोब ऑपरेटर को अंतर्निहित रूप से उस तरह से लागू नहीं करता है जब तक कि shया kshअनुकरण में नहीं। वहां, आपको इसे स्पष्ट रूप से लागू करना होगा। $=stringविभाजित भाग के $~stringलिए, ग्लोब भाग के लिए ( $=~stringदोनों के लिए), और इसमें एक विभाजन ऑपरेटर भी होता है जहाँ आप विभाजक निर्दिष्ट कर सकते हैं:

array=(${(s:_:)string})

या खाली तत्वों को संरक्षित करने के लिए:

array=("${(@s:_:)string}")

ध्यान दें कि वहाँ sके लिए है बंटवारे , नहीं परिसीमन (साथ भी $IFS, का एक ज्ञात POSIX गैर अनुरूपता zsh)। यह जावास्क्रिप्ट से अलग split()है कि एक खाली स्ट्रिंग 0 (1 नहीं) तत्व में विभाजित है।

$IFS-प्लटिंग के साथ एक उल्लेखनीय अंतर यह है कि स्ट्रिंग ${(s:abc:)string}पर विभाजन होता है abc, जबकि, इसके साथ IFS=abcविभाजित होता है a, bया c

साथ zshऔर ksh93, विशेष उपचार अंतरिक्ष, टैब या न्यू लाइन प्राप्त है कि उन्हें में दोगुना से हटाया जा सकता $IFS

एक ऐतिहासिक नोट के रूप में, बॉर्न शेल (पूर्वज या आधुनिक POSIX गोले) ने हमेशा खाली तत्वों को छीन लिया। इसमें गैर-डिफ़ॉल्ट मूल्यों के साथ $ @ के विभाजन और विस्तार से संबंधित कई बग भी थे $IFS। उदाहरण के लिए IFS=_; set -f; set -- $@इसके बराबर नहीं होगा IFS=_; set -f; set -- $1 $2 $3...

Regexps पर विभाजन

अब जावास्क्रिप्ट के करीब कुछ के लिए split()जो नियमित अभिव्यक्तियों पर विभाजित हो सकता है, आपको बाहरी उपयोगिताओं पर भरोसा करना होगा।

POSIX टूल-चेस्ट में, awkएक splitऑपरेटर होता है जो विस्तारित नियमित अभिव्यक्तियों पर विभाजित हो सकता है (जो जावास्क्रिप्ट द्वारा समर्थित पर्ल-रेगुलर रेग्युलर एक्सप्रेशंस का एक उपसमुच्चय है)।

split() {
  awk -v q="'" '
    function quote(s) {
      gsub(q, q "\\" q q, s)
      return q s q
    }
    BEGIN {
      n = split(ARGV[1], a, ARGV[2])
      for (i = 1; i <= n; i++) printf " %s", quote(a[i])
      exit
    }' "$@"
}
string=a__b_+c
eval "array=($(split "$string" '[_+]+'))"

zshखोल पर्ल संगत नियमित अभिव्यक्ति (अपने में के लिए समर्थन अंतर्निहित है zsh/pcre, मॉड्यूल), लेकिन यह का उपयोग कर एक स्ट्रिंग विभाजित करने के लिए संभव है, हालांकि अपेक्षाकृत बोझिल है।


क्या टैब, स्पेस और न्यूलाइन के साथ विशेष उपचार का कोई कारण है?
cuonglm

1
@cuonglm, आम तौर पर आप शब्दों पर विभाजित करना चाहते हैं जब सीमांकक खाली होते हैं, गैर-रिक्त सीमांकक के मामले में (जैसे विभाजन $PATHकरना :) इसके विपरीत, आप आम तौर पर खाली तत्वों को संरक्षित करना चाहते हैं। ध्यान दें कि बॉर्न शेल में, सभी वर्ण विशेष उपचार प्राप्त कर रहे थे, kshजिससे कि केवल खाली वाले (केवल स्थान, टैब और न्यूलाइन हालांकि) का विशेष रूप से इलाज हुआ।
स्टीफन चेजलस

खैर, हाल ही में जोड़े गए बॉर्न शेल नोट ने मुझे चौंका दिया। और पूरा करने के लिए, क्या आपको zshस्ट्रिंग के साथ उपचार के लिए नोट जोड़ना चाहिए जिसमें 2 या अधिक वर्ण हैं ${(s:string:)var}? अगर जोड़ दिया जाए, तो मैं अपना जवाब हटा सकता हूं :)
cuonglm

1
आपका क्या मतलब है "यह भी ध्यान दें कि $ IFS में S डेलिमिटर के लिए है, सेपरेटर के लिए नहीं।" मैं यांत्रिकी समझते हैं और विभाजक लेकिन अनुगामी है कि यह ध्यान नहीं देता है Sके लिए खड़ा है सेपरेटर , नहीं सीमांकक । कम से कम, यह है कि मेरे बैश का मैनुअल क्या कहता है।
terdon

@terdon, $IFSबॉर्न शेल से आता है जहां यह विभाजक था , ksh ने नाम बदले बिना व्यवहार को बदल दिया। मैं उल्लेख करता हूं कि तनाव split+glob(zsh या pdksh को छोड़कर) में अब कोई विभाजन नहीं होता है।
स्टीफन चेजलस

7

हां, इसका उपयोग करें IFSऔर इसे सेट करें _। फिर read -aएक सरणी में स्टोर करने के लिए उपयोग करें ( -rबैकस्लैश विस्तार बंद हो जाता है)। ध्यान दें कि यह बैश के लिए विशिष्ट है; ksh और zsh में थोड़े अलग सिंटैक्स के साथ समान विशेषताएं हैं, और सादे श में सरणी चर बिल्कुल नहीं हैं।

$ r="var1_var2_var3"
$ IFS='_' read -r -a array <<< "$r"
$ for name in "${array[@]}"; do echo "+ $name"; done
+ var1
+ var2
+ var3

से man bash:

पढ़ना

-एक aname

यह शब्द ऐरे वेरिएबल एनेम के अनुक्रमिक सूचकांकों को दिए गए हैं, किसी भी नए मानों को असाइन किए जाने से पहले 0. एनेम शुरू नहीं होता है। अन्य नाम तर्कों की अनदेखी की जाती है।

भारतीय विदेश सेवा

आंतरिक क्षेत्र विभाजक जिसका उपयोग विस्तार के बाद शब्द विभाजन के लिए किया जाता है और रीड बिलिन कमांड के साथ लाइनों को शब्दों में विभाजित किया जाता है। डिफ़ॉल्ट मान `` '' है।

ध्यान दें कि readपहली नई रेखा पर रुकता है। उस से बचने के -d ''लिए पास करें read, लेकिन उस स्थिति में, <<<ऑपरेटर के कारण अंत में एक अतिरिक्त नई रेखा होगी । आप इसे मैन्युअल रूप से निकाल सकते हैं:

IFS='_' read -r -d '' -a array <<< "$r"
array[$((${#array[@]}-1))]=${array[$((${#array[@]}-1))]%?}

यह मानता $rहै कि न्यूलाइन वर्ण या बैकस्लैश नहीं हैं। यह भी ध्यान दें कि यह केवल bashशेल के हाल के संस्करणों में काम करेगा ।
स्टीफन चेजलस

@ स्टीफनचैलेजलस अच्छा बिंदु। हां, यह एक स्ट्रिंग का "मूल" मामला है। बाकी के लिए, सभी को आपके व्यापक उत्तर के लिए जाना चाहिए। के संस्करणों के बारे में bash, read -aबैश 4 में पेश किया गया था, है ना?
महासंघ

1
खेद है कि मेरा बुरा, मुझे लगा कि <<<हाल ही में जोड़ा गया था , bashलेकिन ऐसा लगता है कि यह 2.05 बी (2002) के बाद से है। read -aइससे भी पुराना है। <<<से आता है zshऔर ksh93(और mksh और यश) के रूप में अच्छी तरह से समर्थित है, लेकिन read -aबैश-विशिष्ट (यह -Aksh93, यश और zsh में है)।
स्टीफन चेजलस

@ StéphaneChazelas क्या कोई "आसान" तरीका है जब ये परिवर्तन हुए? मैं कहता हूं कि "आसान" रिलीज की फाइलों में खोदना नहीं है, हो सकता है कि एक पृष्ठ उन सभी को दिखा रहा हो।
महासंघ

1
मैं उसके लिए परिवर्तन लॉग देखता हूं। zsh के पास इतिहास के साथ एक git रिपॉजिटरी भी है जो 3.1.5 के रूप में है और इसकी मेलिंग सूची का उपयोग ट्रैकिंग परिवर्तनों के लिए भी किया जाता है।
स्टीफन चेजलस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.