कैसे बैश में एक सरणी सॉर्ट करने के लिए


139

मेरे पास बैश में एक सरणी है, उदाहरण के लिए:

array=(a c b f 3 5)

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

जवाबों:


208

आपको वास्तव में उस सभी कोड की आवश्यकता नहीं है:

IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS

तत्वों में व्हॉट्सएप का समर्थन करता है (जब तक कि यह एक नई रेखा नहीं है), और बाश 3.x में काम करता है।

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

$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]

नोट: @sorontar है ने बताया कि देखभाल की आवश्यकता होती है, तो तत्व इस तरह के रूप में वाइल्डकार्ड शामिल *या ?:

सॉर्ट किया गया = ($ (...)) भाग "विभाजन और ग्लोब" ऑपरेटर का उपयोग कर रहा है। आपको ग्लोब को बंद करना चाहिए: set -fया set -o noglobया shopt -op noglobसरणी के एक तत्व की तरह *फ़ाइलों की सूची में विस्तारित किया जाएगा।

क्या हो रहा है:

परिणाम एक परिणति छह चीजें हैं जो इस क्रम में होती हैं:

  1. IFS=$'\n'
  2. "${array[*]}"
  3. <<<
  4. sort
  5. sorted=($(...))
  6. unset IFS

पहले IFS=$'\n'

यह हमारे ऑपरेशन का एक महत्वपूर्ण हिस्सा है जो 2 और 5 के परिणाम को निम्न तरीके से प्रभावित करता है:

दिया हुआ:

  • "${array[*]}" के पहले चरित्र द्वारा सीमांकित हर तत्व के लिए फैलता है IFS
  • sorted=() के हर चरित्र पर विभाजित करके तत्व बनाता है IFS

IFS=$'\n' चीजों को सेट करता है ताकि तत्वों का उपयोग करके विस्तार किया जाए एक नई लाइन सीमांकक के रूप में , और फिर बाद में इस तरह से बनाया जाए कि प्रत्येक रेखा एक तत्व बन जाए। (यानी एक नई लाइन में विभाजन।)

नई लाइन द्वारा परिसीमन करना महत्वपूर्ण है क्योंकि यह है कि कैसे sortकाम करता है (प्रति पंक्ति छंटनी)। बंटवारे से ही एक नई लाइन महत्वपूर्ण नहीं है, लेकिन ऐसे तत्वों को संरक्षित करना आवश्यक है जिनमें रिक्त स्थान या टैब शामिल हैं।

का डिफ़ॉल्ट मान IFSहै एक अंतरिक्ष , एक टैब , के बाद एक नई लाइन , और हमारे आपरेशन के लिए अयोग्य हो जाएगा।

इसके बाद द sort <<<"${array[*]}" हिस्सा

<<<, यहाँ तार कहा जाता है , के विस्तार लेता है "${array[*]}", जैसा कि ऊपर बताया गया है, और इसे के मानक इनपुट में खिलाती हैsort

हमारे उदाहरण के साथ, sortयह निम्नलिखित स्ट्रिंग खिलाया जाता है:

a c
b
f
3 5

sort प्रकार के बाद से , यह उत्पादन करता है:

3 5
a c
b
f

अगला,sorted=($(...)) हिस्सा

$(...)हिस्सा है, कहा जाता है आदेश प्रतिस्थापन , उसकी सामग्री (का कारण बनता है sort <<<"${array[*]}, जबकि परिणामस्वरूप लेने,) एक सामान्य आदेश के रूप में चलाने के लिए मानक उत्पादन शाब्दिक रूप में चला जाता है कि जहां कभी $(...)था।

हमारे उदाहरण में, यह केवल लिखने के समान कुछ पैदा करता है:

sorted=(3 5
a c
b
f
)

sorted फिर एक सरणी बन जाती है जो इस शाब्दिक को हर नई पंक्ति में विभाजित करके बनाई जाती है।

अंततः unset IFS

यह IFSडिफ़ॉल्ट मान के मान को रीसेट करता है , और केवल अच्छा अभ्यास है।

यह सुनिश्चित करना है कि हम IFSअपनी स्क्रिप्ट में बाद में निर्भर होने वाली किसी भी चीज़ से परेशानी न करें । (अन्यथा हमें याद रखना होगा कि हमने चीजों को चारों ओर बदल दिया है - ऐसा कुछ जो जटिल लिपियों के लिए अव्यावहारिक हो सकता है।)


2
@xxor बिना IFS, यह आपके तत्वों को छोटे टुकड़ों में विभाजित कर देगा यदि उनमें व्हाट्सएप है। लोप के साथ उदाहरण की कोशिश करो IFS=$'\n' और देखो!
एंटक

3
बहुत अच्छा। क्या आप औसत बैश उपयोगकर्ता के लिए समझा सकते हैं कि यह समाधान कैसे काम करता है?
u32004

2
अब, इसके साथ IFS, यह आपके तत्वों को छोटे टुकड़ों में विभाजित करता है यदि उनके पास केवल एक विशेष प्रकार का व्हाट्सएप हो। अच्छा; नहीं सही :-)
सीमित प्रायश्चित

7
है unset IFSआवश्यक? मैंने सोचा था कि IFS=एक कमांड को प्रस्तुत करने से केवल उस कमांड में परिवर्तन होता है, बाद में अपने पिछले मूल्य पर अपने आप वापस आ जाता है।
मार्क एच

10
@ मार्ख यह आवश्यक है क्योंकि sorted=()एक कमांड नहीं है, बल्कि एक दूसरा चर असाइनमेंट है।
एंटक

35

मूल प्रतिक्रिया:

array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)

उत्पादन:

$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f

इस संस्करण को उन मानों के साथ नोट करें, जिनमें विशेष वर्ण या व्हाट्सएप शामिल हैं (नए सिरे को छोड़कर )

नोट रीडअरे को बैश 4+ में समर्थित है।


संपादित करें @Dimitre द्वारा सुझाव के आधार पर मैंने इसे अपडेट किया था:

readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)

जिसमें सही ढंग से एम्बेडेड newline वर्णों के साथ छँटाई तत्वों को समझने का भी लाभ है। दुर्भाग्य से, जैसा कि @ruakh द्वारा सही संकेत दिया गया था, इसका मतलब यह नहीं था कि परिणाम का परिणाम सहीreadarray होगा , क्योंकि लाइन-सेपरेटर के रूप में नियमित रूप से नईलाइन्स के बजाय उपयोग करने का कोई विकल्प नहीं है ।readarrayNUL


5
अच्छा, यह भी ध्यान दिया जाना चाहिए कि बैश के संस्करण 4 के बाद से रीडअरे उपलब्ध है। इसे थोड़ा छोटा किया जा सकता है:readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
दिमित्रे रादोलोव

1
@Dimitre: मैंने आपका सुझाव लिया और किसी भी चीज़ के साथ काम करने के लिए व्हॉट्सएप को ठीक किया (आंतरिक रूप से नलचर-सीमांकक का उपयोग करके)। चीयर्स
sehe

1
हां, sort -zयह एक उपयोगी सुधार है, मुझे लगता है कि -zविकल्प एक GNU सॉर्ट एक्सटेंशन है।
दिमित्रे रादोलोव

2
यदि आप एम्बेडेड newlines को संभालना चाहते हैं, तो आप अपनी खुद की रीडायरे को रोल कर सकते हैं। उदाहरण के लिए sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z):। यह भी आप में काम करता है bash v4 के बजाय bash v3 का उपयोग कर रहा है, क्योंकि bash v3 में रीडअरे उपलब्ध नहीं है।
बॉब बेल

1
@ user1527227 यह इनपुट पुनर्निर्देशन ( <) प्रक्रिया प्रतिस्थापन के साथ संयुक्त है <(...)। या इसे सहज रूप से रखने के लिए: क्योंकि (printf "bla")कोई फ़ाइल नहीं है।
sehe

33

यहां एक शुद्ध बैश क्विकॉर्ट कार्यान्वयन है:

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
qsort() {
   local pivot i smaller=() larger=()
   qsort_ret=()
   (($#==0)) && return 0
   pivot=$1
   shift
   for i; do
      if (( i < pivot )); then
         smaller+=( "$i" )
      else
         larger+=( "$i" )
      fi
   done
   qsort "${smaller[@]}"
   smaller=( "${qsort_ret[@]}" )
   qsort "${larger[@]}"
   larger=( "${qsort_ret[@]}" )
   qsort_ret=( "${smaller[@]}" "$pivot" "${larger[@]}" )
}

के रूप में उपयोग करें, जैसे,

$ array=(a c b f 3 5)
$ qsort "${array[@]}"
$ declare -p qsort_ret
declare -a qsort_ret='([0]="3" [1]="5" [2]="a" [3]="b" [4]="c" [5]="f")'

यह कार्यान्वयन पुनरावर्ती है ... इसलिए यहां एक पुनरावृत्त क्विकॉर्ट है:

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
qsort() {
   (($#==0)) && return 0
   local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
   qsort_ret=("$@")
   while ((${#stack[@]})); do
      beg=${stack[0]}
      end=${stack[1]}
      stack=( "${stack[@]:2}" )
      smaller=() larger=()
      pivot=${qsort_ret[beg]}
      for ((i=beg+1;i<=end;++i)); do
         if [[ "${qsort_ret[i]}" < "$pivot" ]]; then
            smaller+=( "${qsort_ret[i]}" )
         else
            larger+=( "${qsort_ret[i]}" )
         fi
      done
      qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
      if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
      if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
   done
}

दोनों मामलों में, आप अपने द्वारा उपयोग किए जाने वाले आदेश को बदल सकते हैं: मैंने स्ट्रिंग तुलनाओं का उपयोग किया था, लेकिन आप अंकगणितीय तुलनाओं का उपयोग कर सकते हैं, wrt फ़ाइल संशोधन समय की तुलना कर सकते हैं, आदि बस उपयुक्त परीक्षण का उपयोग करें; आप इसे और भी सामान्य बना सकते हैं और इसमें पहले तर्क का उपयोग किया जाता है जो कि टेस्ट फ़ंक्शन का उपयोग है, जैसे;

#!/bin/bash

# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
# First argument is a function name that takes two arguments and compares them
qsort() {
   (($#<=1)) && return 0
   local compare_fun=$1
   shift
   local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
   qsort_ret=("$@")
   while ((${#stack[@]})); do
      beg=${stack[0]}
      end=${stack[1]}
      stack=( "${stack[@]:2}" )
      smaller=() larger=()
      pivot=${qsort_ret[beg]}
      for ((i=beg+1;i<=end;++i)); do
         if "$compare_fun" "${qsort_ret[i]}" "$pivot"; then
            smaller+=( "${qsort_ret[i]}" )
         else
            larger+=( "${qsort_ret[i]}" )
         fi
      done
      qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
      if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
      if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
   done
}

तब आपके पास यह तुलनात्मक कार्य हो सकता है:

compare_mtime() { [[ $1 -nt $2 ]]; }

और उपयोग करें:

$ qsort compare_mtime *
$ declare -p qsort_ret

संशोधन समय (नवीनतम पहले) द्वारा क्रमबद्ध फ़ोल्डर में फ़ाइलें हैं।

ध्यान दें। ये कार्य शुद्ध बैश हैं! कोई बाहरी उपयोगिताओं, और कोई सदस्यता नहीं! वे सुरक्षित हो सकते हैं किसी भी अजीब प्रतीकों आप (रिक्त स्थान, newline वर्ण, ग्लोब वर्ण, आदि) हो सकता है।


1
प्रभावशाली बोशिंग के लिए यश जो इनपुट तत्वों और सॉर्ट मानदंडों के संबंध में शानदार लचीलापन प्रदान करता है। अगर लाइन-आधारित सॉर्टिंग के साथ जो विकल्प sortप्रदान करता है वह पर्याप्त है, तो sort+ read -aसमाधान तेजी से चारों ओर शुरू हो जाएगा, कहते हैं, 20 आइटम, और तेजी से और अधिक तेजी से और अधिक तत्व आप के साथ काम कर रहे हैं। उदाहरण के लिए, 2012 के अंत में iMac रनिंग OSX 10.11.1 एक फ्यूजन ड्राइव के साथ: 100-तत्व सरणी: ca. 0.03 सेकेंड। ( qsort()) बनाम सीए। 0.005 सेकेंड। ( sort+ read -a); 1000-तत्व सरणी: ca. 0.375 सेकेंड। ( qsort()) बनाम सीए। 0.014 सेकंड ( sort+ read -a)।
mklement0

अच्छा लगा। मुझे कॉलेज के दिनों से जल्दी याद है लेकिन बबल सॉर्ट पर भी शोध करेंगे। मेरी छंटनी की जरूरतों के लिए मेरे पास पहले और दूसरे तत्व हैं जो एक डेटा तत्व के बाद कुंजी बनाते हैं (जिसका मैं बाद में विस्तार कर सकता हूं)। आपके कोड को प्रमुख तत्वों (parm1) और डेटा तत्वों की संख्या (parm2) के साथ बेहतर बनाया जा सकता है। ओपी के लिए पैरामीटर 1 और 0. होगा। मेरे लिए पैरामीटर 2 और 1 होगा। किसी भी संबंध में आपके उत्तर में सबसे अधिक वादा है।
विनयुनुच्स 2 यूनिक्स

1
Uncast स्ट्रिंग पूर्णांक के एक डेटासेट के साथ मुझे पाया if [ "$i" -lt "$pivot" ]; thenगया था अन्यथा अन्यथा "2" <"10" सही था। मेरा मानना ​​है कि यह POSIX बनाम लेक्सिकोग्राफिक है; या शायद इनलाइन लिंक
Page2PagePro

27

यदि आपको सरणी तत्वों में विशेष शेल वर्णों को संभालने की आवश्यकता नहीं है:

array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))

बैश के साथ आपको वैसे भी एक बाहरी छँटाई कार्यक्रम की आवश्यकता होगी।

Zsh के साथ किसी बाहरी कार्यक्रम की आवश्यकता नहीं होती है और विशेष शैल पात्रों को आसानी से नियंत्रित किया जाता है:

% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}" 
3
5
a a
b
c
f

ksh को ASCIIbeticallyset -s को सॉर्ट करना होगा


बहुत अच्छी पृष्ठभूमि की जानकारी। मैं लगभग एक डेमो के बारे में पूछूंगा कि ksh सेट-फ्लैग का उपयोग कैसे करेगा ... लेकिन फिर, सवाल बैश पर है, इसलिए वह ऑफ-टॉपिक होगा
sehe

यह सबसे KornShell कार्यान्वयन के साथ काम करना चाहिए (उदाहरण के लिए ksh88 और pdksh ): set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@" और, निश्चित रूप से, सेट कमांड वर्तमान स्थितीय मापदंडों को रीसेट कर देगा, यदि कोई हो।
दिमित्रे रादोलोव

आप खोल ज्ञान के एक सत्यनिष्ठ फव्वारे हैं। मुझे यकीन है कि आपके पास फोटोग्राफिक मेमोरी या कुछ और होना चाहिए, क्योंकि इस तरह के सूक्ष्म अंतर मानव प्रजातियों के अन्य सदस्यों में से अधिकांश को बाहर कर देते हैं :), +1 जानकारी के पूर्ण पैकेज के लिए
sehe

10

टीएल; डीआर :

क्रमबद्ध सरणी a_inऔर में परिणाम की दुकान a_out(तत्वों नहीं होना चाहिए एम्बेडेड नई-पंक्तियों [1] ):

बैश v4 +:

readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)

बैश v3:

IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)

एंटाक के समाधान पर लाभ :

  • आपको आकस्मिक ग्लोबिंग (फ़ाइल नाम पैटर्न के रूप में सरणी तत्वों की आकस्मिक व्याख्या) के बारे में चिंता करने की ज़रूरत नहीं है, इसलिए ग्लोबिंग को अक्षम करने के लिए कोई अतिरिक्त आदेश की आवश्यकता नहीं है ( set -fऔर set +fबाद में इसे पुनर्स्थापित करने के लिए)।

  • आप के IFSसाथ रीसेट करने के बारे में चिंता करने की ज़रूरत नहीं है unset IFS[2]


वैकल्पिक पढ़ने: स्पष्टीकरण और नमूना कोड

उपरोक्त sortएक समाधान के लिए बाहरी उपयोगिता के साथ बैश कोड को जोड़ती है जो मनमाने ढंग से सिंगल- लाइन तत्वों और या तो लेक्सिकल या संख्यात्मक छँटाई के साथ काम करता है (वैकल्पिक रूप से फ़ील्ड द्वारा) :

  • प्रदर्शन : लगभग 20 तत्वों या उससे अधिक के लिए , यह शुद्ध बैश समाधान की तुलना में तेज होगा - महत्वपूर्ण और तेजी से एक बार जब आप लगभग 100 तत्वों से परे हो जाते हैं।
    (सटीक सीमा आपके विशिष्ट इनपुट, मशीन और प्लेटफॉर्म पर निर्भर करेगी।)

    • इसका कारण यह है कि यह बैश लूप्स से बचा जाता है
  • printf '%s\n' "${a_in[@]}" | sort सॉर्टिंग निष्पादित करता है (शाब्दिक रूप से, डिफ़ॉल्ट रूप से - sortPOSIX कल्पना देखें ):

    • "${a_in[@]}"सुरक्षित रूप से सरणी के तत्वों के लिए विस्तारित a_inरूप में अलग-अलग तर्क , जो कुछ भी वे होते हैं (व्हॉट्सएप सहित)।

    • printf '%s\n' फिर प्रत्येक तर्क को प्रिंट करता है - अर्थात, प्रत्येक सरणी तत्व - अपनी लाइन पर, जैसा कि है।

  • नोट एक के उपयोग (प्रक्रिया प्रतिस्थापन <(...)) को इनपुट के रूप में क्रमबद्ध उत्पादन प्रदान करने के लिए read/ readarray(stdin, करने के लिए पुनर्निर्देशन के माध्यम से <,) क्योंकि read/ readarrayमें चलाना चाहिए वर्तमान खोल (एक में नहीं चलाया जाना चाहिए subshell उत्पादन चर के लिए) क्रम में a_outदिखाई दे सकता है वर्तमान शेल (स्क्रिप्ट के शेष भाग में परिभाषित किए जाने वाले चर के लिए)।

  • sortएक सरणी चर में आउटपुट पढ़ना :

    • बैश v4 +: प्रत्येक तत्व ( ) में अनुगामी को शामिल किए बिना, सरणी चर के तत्वों में readarray -t a_outव्यक्तिगत लाइनों के आउटपुट को पढ़ता है ।sorta_out\n-t

    • बैश v3: readarrayमौजूद नहीं है, इसलिए readइसका उपयोग किया जाना चाहिए: सरणी ( ) चर में पढ़ने के लिए
      IFS=$'\n' read -d '' -r -a a_outकहता readहै , पूरे इनपुट को पढ़ रहा है, लाइनों के पार ( ), लेकिन इसे नए तत्वों द्वारा सरणी तत्वों में विभाजित करना ( !) , जो एक शाब्दिक नई रेखा (LF) का उत्पादन करता है । ), एक तथाकथित एएनएसआई सी-उद्धृत स्ट्रिंग है )। ( , एक ऐसा विकल्प, जिसे वस्तुतः हमेशा उपयोग किया जाना चाहिए , वर्णों की अप्रत्याशित हैंडलिंग को अक्षम करता है ।)-aa_out-d ''IFS=$'\n'$'\n'
      -rread\

एनोटेट नमूना कोड:

#!/usr/bin/env bash

# Define input array `a_in`:
# Note the element with embedded whitespace ('a c')and the element that looks like
# a glob ('*'), chosen to demonstrate that elements with line-internal whitespace
# and glob-like contents are correctly preserved.
a_in=( 'a c' b f 5 '*' 10 )

# Sort and store output in array `a_out`
# Saving back into `a_in` is also an option.
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
# Bash 4.x: use the simpler `readarray -t`:
# readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)

# Print sorted output array, line by line:
printf '%s\n' "${a_out[@]}"

sortबिना विकल्पों के उपयोग के कारण , यह लेक्सिकल पैदा करता है सॉर्टिंग (अंकों से पहले अंक सॉर्ट करता है, और अंकों के अनुक्रमों को लेक्सिक रूप से माना जाता है, संख्याओं के रूप में नहीं):

*
10
5
a c
b
f

यदि आप 1 क्षेत्र द्वारा संख्यात्मक छँटाई चाहते थे , तो आप sort -k1,1nकेवल sortपैदावार का उपयोग करेंगे , जो पैदावार (संख्याओं से पहले गैर-क्रमबद्ध क्रमांक, और संख्याएँ सही प्रकार से होती हैं):

*
a c
b
f
5
10

[१] एंबेडेड न्यूलाइन्स वाले तत्वों को संभालने के लिए, निम्न प्रकार (बश v4 +, GNU के साथ sort) का उपयोग करें
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z):।
मिशैल गौरी के उपयोगी उत्तर में बैश v3 समाधान है।

[2] जबकि IFS है बैश v3 संस्करण में सेट, परिवर्तन है आदेश के दायरे वाला
इसके विपरीत, IFS=$'\n' एंटक के उत्तर में जो कुछ होता है वह एक कमांड के बजाय एक असाइनमेंट है , जिसमें IFSपरिवर्तन वैश्विक है


8

म्यूनिख से फ्रैंकफर्ट तक की 3-घंटे की ट्रेन यात्रा में (जो मुझे पहुंचने में परेशानी थी क्योंकि ओकट्रैफेस्ट कल शुरू होता है) मैं अपनी पहली पोस्ट के बारे में सोच रहा था। एक वैश्विक सरणी को नियोजित करना एक सामान्य सॉर्ट फ़ंक्शन के लिए एक बेहतर विचार है। निम्नलिखित फ़ंक्शन मध्यस्थ स्ट्रिंग्स (newlines, blanks आदि) को संभालता है:

declare BSORT=()
function bubble_sort()
{   #
    # @param [ARGUMENTS]...
    #
    # Sort all positional arguments and store them in global array BSORT.
    # Without arguments sort this array. Return the number of iterations made.
    #
    # Bubble sorting lets the heaviest element sink to the bottom.
    #
    (($# > 0)) && BSORT=("$@")
    local j=0 ubound=$((${#BSORT[*]} - 1))
    while ((ubound > 0))
    do
        local i=0
        while ((i < ubound))
        do
            if [ "${BSORT[$i]}" \> "${BSORT[$((i + 1))]}" ]
            then
                local t="${BSORT[$i]}"
                BSORT[$i]="${BSORT[$((i + 1))]}"
                BSORT[$((i + 1))]="$t"
            fi
            ((++i))
        done
        ((++j))
        ((--ubound))
    done
    echo $j
}

bubble_sort a c b 'z y' 3 5
echo ${BSORT[@]}

यह प्रिंट:

3 5 a b c z y

उसी आउटपुट से बनाया गया है

BSORT=(a c b 'z y' 3 5) 
bubble_sort
echo ${BSORT[@]}

ध्यान दें कि शायद बैश आंतरिक रूप से स्मार्ट-पॉइंटर्स का उपयोग करता है, इसलिए स्वैप-ऑपरेशन सस्ता हो सकता है (हालांकि मुझे संदेह है)। हालाँकि, bubble_sortप्रदर्शित करता है कि अधिक उन्नत कार्य merge_sortभी शेल भाषा की पहुंच में हैं।


5
बबल शॅाट? वाह .. ओबामा का कहना है "बुलबुला
छंटने

1
हालांकि, ऐसा लगता है कि जब ओ-स्मार्ट स्मार्ट होना चाहता था तो उसे होश नहीं था कि यह 50/50 का मौका सवाल नहीं है। O-guy की स्थिति में एक पूर्ववर्ती, चलो उसे B-guy बताते हैं, एक बार बहुत बेहतर किया (रेनॉल्ड्सबर्ग, ओहियो, अक्टूबर 2000): "मुझे लगता है कि यदि आप जानते हैं कि आप क्या मानते हैं, तो सवालों का जवाब देना बहुत आसान हो जाता है मैं आपके सवाल का जवाब नहीं दे सकता। तो यह B- लड़का वास्तव में बूलियन तर्क के बारे में कुछ जानता है। हे लड़का नहीं है
एंड्रियास स्पिंडलर

फ़ंक्शन को आसानी से पोर्टेबल बनाया जा सकता है बीएसएनआर को एक स्थानीय सरणी बनाकर जो किसी भी सरणी को सॉर्ट किया जाना है। यानी local -n BSORT="$1"फंक्शन की शुरुआत में। तब आप मायार्रेbubble_sort myarray को सॉर्ट करने के लिए चला सकते हैं ।
२३:१३ पर जॉनऑर्फ

7

एक और समाधान जो किसी विशेष वर्ण (NULs को छोड़कर) के sortसाथ बाहरी और कॉप्स का उपयोग करता है । बैश -3.5 और जीएनयू या बीएसडी के साथ काम करना चाहिए (दुख की बात है, पोसिक्स शामिल नहीं है )।sort-z

local e new_array=()
while IFS= read -r -d '' e; do
    new_array+=( "${e}" )
done < <(printf "%s\0" "${array[@]}" | LC_ALL=C sort -z)

सबसे पहले इनपुट पुनर्निर्देशन को अंत में देखें। हम printfएरे तत्वों को लिखने के लिए बिल्ट-इन का उपयोग कर रहे हैं, शून्य-समाप्त। उद्धृत करना सुनिश्चित करता है कि ऐरे तत्व एलीमेंट के रूप में पास किए गए हैं, और शेल की बारीकियों के printfकारण इसे अपने शेष पैरामीटर के लिए फॉर्मेट स्ट्रिंग के अंतिम भाग का पुन: उपयोग करना है। अर्थात्, यह कुछ के समान है:

for e in "${array[@]}"; do
    printf "%s\0" "${e}"
done

नल-समाप्त तत्व सूची तब पास की जाती है sort-zविकल्प अशक्त-समाप्त तत्वों, तरह उन्हें और उत्पादन में अच्छी तरह से अशक्त-समाप्त पढ़ने के लिए यह कारण बनता है। यदि आपको केवल अनन्य तत्व प्राप्त करने की आवश्यकता है, तो आप पास कर सकते हैं -uक्योंकि यह अधिक पोर्टेबल है uniq -zLC_ALL=Cस्थान की स्वतंत्र रूप से स्थिर सॉर्ट क्रम सुनिश्चित करता है - कभी कभी स्क्रिप्ट के लिए उपयोगी। अगर तुम चाहो तोsort लोकेल का सम्मान , तो उसे हटा दें।

<()निर्माण वर्णनकर्ता प्राप्त पैदा की पाइप लाइन से पढ़ने के लिए, और <के मानक इनपुट पुनर्निर्देशwhile इसे करने के लिए पाश। यदि आपको पाइप के अंदर मानक इनपुट तक पहुंचने की आवश्यकता है, तो आप एक और विवरणक का उपयोग कर सकते हैं - पाठक के लिए व्यायाम :)।

अब, शुरुआत में वापस। readनिर्मित में रीडायरेक्ट stdin से उत्पादन पढ़ता है। खाली IFSविभाजन शब्द विभाजन को सेट करना जो यहां अनावश्यक है - परिणामस्वरूप, readएकल प्रदान किए गए चर के लिए इनपुट की पूरी 'लाइन' पढ़ता है। -rविकल्प यहां से अवांछित होने के साथ प्रसंस्करण से भी बच जाता है। अंत में, -d ''रेखा परिसीमन NUL को सेट करता है - जो बताता हैread शून्य-समाप्त स्ट्रिंग्स को पढ़ना है।

नतीजतन, लूप को प्रत्येक क्रमिक शून्य-समाप्त सरणी तत्व के लिए एक बार निष्पादित किया जाता है, जिसमें मूल्य को संग्रहीत किया जाता है e। उदाहरण सिर्फ वस्तुओं को दूसरे सरणी में रखता है, लेकिन आप उन्हें सीधे संसाधित करना पसंद कर सकते हैं :)।

बेशक, यह एक ही लक्ष्य को प्राप्त करने के कई तरीकों में से एक है। जैसा कि मैं इसे देखता हूं, यह पूरी तरह से छंटाई में एल्गोरिथ्म को लागू करने की तुलना में सरल है और कुछ मामलों में यह तेज होगा। यह newlines सहित सभी विशेष पात्रों को संभालता है और अधिकांश सामान्य प्रणालियों पर काम करना चाहिए। सबसे महत्वपूर्ण बात, यह आपको बैश के बारे में कुछ नया और भयानक सिखा सकता है :)।


महान समाधान और बहुत उपयोगी स्पष्टीकरण, धन्यवाद। एक विस्तार: IFS को खाली करने के बिना, प्रमुख व्हाट्सएप को भी समाप्त कर दिया जाएगा - भले ही कोई शब्द विभाजन नहीं किया गया हो।
डिर्क हेरमैन

स्थानीय वैरिएबल को शुरू eकरने और खाली IFS को सेट करने के बजाय , REPLY वैरिएबल का उपयोग करें।
रॉबिन ए। मीड

2

इसे इस्तेमाल करे:

echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort

आउटपुट होगा:

3
5
ए
ख
सी
च

समस्या सुलझ गयी।


3
अपने सवाल का पूरी तरह से जवाब देने के लिए आउटपुट को एक नए सरणी में डालने के लिए इसे संपादित करना चाहिए।
पीटर ओराम

2

यदि आप सरणी में प्रत्येक तत्व के लिए एक अद्वितीय पूर्णांक की गणना कर सकते हैं, जैसे:

tab='0123456789abcdefghijklmnopqrstuvwxyz'

# build the reversed ordinal map
for ((i = 0; i < ${#tab}; i++)); do
    declare -g ord_${tab:i:1}=$i
done

function sexy_int() {
    local sum=0
    local i ch ref
    for ((i = 0; i < ${#1}; i++)); do
        ch="${1:i:1}"
        ref="ord_$ch"
        (( sum += ${!ref} ))
    done
    return $sum
}

sexy_int hello
echo "hello -> $?"
sexy_int world
echo "world -> $?"

फिर, आप इन पूर्णांक को सरणी अनुक्रमित के रूप में उपयोग कर सकते हैं, क्योंकि बैश हमेशा विरल सरणी का उपयोग करते हैं, इसलिए अप्रयुक्त अनुक्रमितों के बारे में चिंता करने की कोई आवश्यकता नहीं है:

array=(a c b f 3 5)
for el in "${array[@]}"; do
    sexy_int "$el"
    sorted[$?]="$el"
done

echo "${sorted[@]}"
  • पेशेवरों। तेज।
  • विपक्ष। डुप्लिकेट किए गए तत्वों को मर्ज किया जाता है, और 32-बिट अद्वितीय पूर्णांकों के लिए सामग्री को मैप करना असंभव हो सकता है।

दिलचस्प तकनीक, मैंने स्पष्ट तुलना / प्रकार के बिना अधिकतम / मिनट मूल्यों को खोजने के लिए एक संस्करण का उपयोग किया है। लेकिन , लंबाई के संबंध में बिना-भार वाला जोड़ काम नहीं करेगा: "आ" से पहले "z" प्रकार, इसलिए आप इसे शब्दों के लिए उपयोग नहीं कर सकते हैं जैसा कि आप ऊपर दिखाते हैं।
mr.spuratic

2

न्यूनतम सॉर्ट:

#!/bin/bash
array=(.....)
index_of_element1=0

while (( ${index_of_element1} < ${#array[@]} )); do

    element_1="${array[${index_of_element1}]}"

    index_of_element2=$((index_of_element1 + 1))
    index_of_min=${index_of_element1}

    min_element="${element_1}"

        for element_2 in "${array[@]:$((index_of_element1 + 1))}"; do
            min_element="`printf "%s\n%s" "${min_element}" "${element_2}" | sort | head -n+1`"      
            if [[ "${min_element}" == "${element_2}" ]]; then
                index_of_min=${index_of_element2}
            fi
            let index_of_element2++
        done

        array[${index_of_element1}]="${min_element}"
        array[${index_of_min}]="${element_1}"

    let index_of_element1++
done

1
array=(a c b f 3 5)
new_array=($(echo "${array[@]}" | sed 's/ /\n/g' | sort))    
echo ${new_array[@]}

new_array की गूंज सामग्री होगी:

3 5 a b c f

1

रिक्त स्थान और newlines की सामान्य समस्या के लिए एक समाधान है:

एक चरित्र है कि नहीं है (जैसे मूल सरणी में प्रयोग करें $'\1'या $'\4'या समान)।

इस कार्य को पूरा किया जाता है:

# Sort an Array may have spaces or newlines with a workaround (wa=$'\4')
sortarray(){ local wa=$'\4' IFS=''
             if [[ $* =~ [$wa] ]]; then
                 echo "$0: error: array contains the workaround char" >&2
                 exit 1
             fi

             set -f; local IFS=$'\n' x nl=$'\n'
             set -- $(printf '%s\n' "${@//$nl/$wa}" | sort -n)
             for    x
             do     sorted+=("${x//$wa/$nl}")
             done
       }

यह सरणी को सॉर्ट करेगा:

$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>

यह शिकायत करेगा कि स्रोत सरणी में वर्कअराउंड वर्ण है:

$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char

विवरण

  • हम दो स्थानीय चर wa(वर्कअराउंड चार) और एक अशक्त IFS सेट करते हैं
  • तब (ifs null के साथ) हम परीक्षण करते हैं कि पूरी सरणी $*
  • किसी भी चिंता चार शामिल नहीं है [[ $* =~ [$wa] ]]
  • यदि ऐसा होता है, तो एक संदेश बढ़ाएं और एक त्रुटि का संकेत दें: exit 1
  • फ़ाइल नाम विस्तार से बचें: set -f
  • IFS का नया मान ( IFS=$'\n') एक लूप वेरिएबल xऔर एक नया संस्करण ( ) सेट करें nl=$'\n'
  • हम प्राप्त तर्कों (इनपुट सरणी $@) के सभी मूल्यों को प्रिंट करते हैं ।
  • लेकिन हम किसी भी नई लाइन को वर्कअराउंड चार द्वारा बदल देते हैं "${@//$nl/$wa}"
  • उन मूल्यों को हल करने के लिए भेजें sort -n
  • और स्थितिगत तर्कों में सभी क्रमबद्ध मानों को वापस रखें set --
  • फिर हम प्रत्येक तर्क को एक-एक करके (नए सिरे से संरक्षित करने के लिए) असाइन करते हैं।
  • एक पाश में for x
  • एक नए सरणी में: sorted+=(…)
  • किसी भी मौजूदा न्यूलाइन को संरक्षित करने के लिए उद्धरण के अंदर।
  • वर्कलाइन को एक नई स्थिति में पुनर्स्थापित करना "${x//$wa/$nl}"
  • किया हुआ

1

यह प्रश्न निकट से संबंधित है। और बीटीडब्ल्यू, यहां बैश में एक मर्ज है (बाहरी प्रक्रियाओं के बिना):

mergesort() {
  local -n -r input_reference="$1"
  local -n output_reference="$2"
  local -r -i size="${#input_reference[@]}"
  local merge previous
  local -a -i runs indices
  local -i index previous_idx merged_idx \
           run_a_idx run_a_stop \
           run_b_idx run_b_stop

  output_reference=("${input_reference[@]}")
  if ((size == 0)); then return; fi

  previous="${output_reference[0]}"
  runs=(0)
  for ((index = 0;;)) do
    for ((++index;; ++index)); do
      if ((index >= size)); then break 2; fi
      if [[ "${output_reference[index]}" < "$previous" ]]; then break; fi
      previous="${output_reference[index]}"
    done
    previous="${output_reference[index]}"
    runs+=(index)
  done
  runs+=(size)

  while (("${#runs[@]}" > 2)); do
    indices=("${!runs[@]}")
    merge=("${output_reference[@]}")
    for ((index = 0; index < "${#indices[@]}" - 2; index += 2)); do
      merged_idx=runs[indices[index]]
      run_a_idx=merged_idx
      previous_idx=indices[$((index + 1))]
      run_a_stop=runs[previous_idx]
      run_b_idx=runs[previous_idx]
      run_b_stop=runs[indices[$((index + 2))]]
      unset runs[previous_idx]
      while ((run_a_idx < run_a_stop && run_b_idx < run_b_stop)); do
        if [[ "${merge[run_a_idx]}" < "${merge[run_b_idx]}" ]]; then
          output_reference[merged_idx++]="${merge[run_a_idx++]}"
        else
          output_reference[merged_idx++]="${merge[run_b_idx++]}"
        fi
      done
      while ((run_a_idx < run_a_stop)); do
        output_reference[merged_idx++]="${merge[run_a_idx++]}"
      done
      while ((run_b_idx < run_b_stop)); do
        output_reference[merged_idx++]="${merge[run_b_idx++]}"
      done
    done
  done
}

declare -ar input=({z..a}{z..a})
declare -a output

mergesort input output

echo "${input[@]}"
echo "${output[@]}"

0

मुझे यकीन नहीं है कि आपको बैश में एक बाहरी छँटाई कार्यक्रम की आवश्यकता होगी।

यहाँ सरल बुलबुला-सॉर्ट एल्गोरिथ्म के लिए मेरा कार्यान्वयन है।

function bubble_sort()
{   #
    # Sorts all positional arguments and echoes them back.
    #
    # Bubble sorting lets the heaviest (longest) element sink to the bottom.
    #
    local array=($@) max=$(($# - 1))
    while ((max > 0))
    do
        local i=0
        while ((i < max))
        do
            if [ ${array[$i]} \> ${array[$((i + 1))]} ]
            then
                local t=${array[$i]}
                array[$i]=${array[$((i + 1))]}
                array[$((i + 1))]=$t
            fi
            ((i += 1))
        done
        ((max -= 1))
    done
    echo ${array[@]}
}

array=(a c b f 3 5)
echo " input: ${array[@]}"
echo "output: $(bubble_sort ${array[@]})"

यह प्रिंट करेगा:

 input: a c b f 3 5
output: 3 5 a b c f

बबल सॉर्ट है O(n^2)। मुझे याद है कि अधिकांश छंटाई एल्गोरिदम O(n lg(n))अंतिम दर्जन तत्वों तक का उपयोग करते हैं। अंतिम तत्वों के लिए, चयन प्रकार का उपयोग किया जाता है।
jww


-1

sorted=($(echo ${array[@]} | tr " " "\n" | sort))

बैश / लिनेक्स की भावना में, मैं प्रत्येक चरण के लिए सर्वश्रेष्ठ कमांड-लाइन टूल को पाइप करूंगा। sortमुख्य काम करता है, लेकिन अंतरिक्ष के बजाय न्यूलाइन द्वारा अलग किए गए इनपुट की आवश्यकता होती है, इसलिए बहुत सरल पाइपलाइन ऊपर बस करता है:

इको सरणी सामग्री -> स्थान को न्यूलाइन द्वारा बदलें -> सॉर्ट करें

$() परिणाम को प्रतिध्वनित करना है

($()) एक सरणी में "गूंज परिणाम" डाल दिया है

नोट : जैसा कि @sorontar ने एक अलग प्रश्न के लिए एक टिप्पणी में उल्लेख किया है :

सॉर्ट किया गया = ($ (...)) भाग "विभाजन और ग्लोब" ऑपरेटर का उपयोग कर रहा है। आपको ग्लोब को बंद कर देना चाहिए: सेट -f या सेट -o noglob या shopt -op noglob या एरे का एक तत्व जैसे * फ़ाइलों की सूची में विस्तारित किया जाएगा।


बैश / लिनेक्स की भावना में : मुझे लगता है कि आपने आत्मा को बिल्कुल नहीं समझा था। आपका कोड पूरी तरह से टूट गया है (पथनाम विस्तार और शब्द विभाजन)। यह बेहतर होगा (बाशो 4):, mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort)अन्यथा sorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort)
ग्नौरफ_ग्निऑरफ

आपके द्वारा उपयोग किए जा रहे एंटीपैटर्न echo ${array[@]} | tr " " "\n"इस प्रकार हैं: यदि एरे के खेतों में व्हॉट्सएप और ग्लोब वर्ण हैं तो यह टूट जाएगा। इसके अलावा, यह एक उपधारा को जन्म देता है और एक बेकार बाहरी कमांड का उपयोग करता है। और echoगूंगा होने के कारण , यह टूट जाएगा यदि आपकी सरणी शुरू होती है -e, -Eया -n। इसके बजाय का उपयोग करें: printf '%s\n' "${array[@]}"। अन्य एंटीपैटर्न है: ($())एक सरणी में "प्रतिध्वनि परिणाम" डालना । हरगिज नहीं! यह एक भयानक एंटीपैटर्न है जो पाथनाम विस्तार (ग्लोबिंग) और शब्द विभाजन के कारण टूट जाता है। कभी भी इस डरावनी का उपयोग न करें।
ग्नौरफ_ग्निऑरफ

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