जवाबों:
आपको वास्तव में उस सभी कोड की आवश्यकता नहीं है:
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
सरणी के एक तत्व की तरह*
फ़ाइलों की सूची में विस्तारित किया जाएगा।
परिणाम एक परिणति छह चीजें हैं जो इस क्रम में होती हैं:
IFS=$'\n'
"${array[*]}"
<<<
sort
sorted=($(...))
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
अपनी स्क्रिप्ट में बाद में निर्भर होने वाली किसी भी चीज़ से परेशानी न करें । (अन्यथा हमें याद रखना होगा कि हमने चीजों को चारों ओर बदल दिया है - ऐसा कुछ जो जटिल लिपियों के लिए अव्यावहारिक हो सकता है।)
IFS
, यह आपके तत्वों को छोटे टुकड़ों में विभाजित करता है यदि उनके पास केवल एक विशेष प्रकार का व्हाट्सएप हो। अच्छा; नहीं सही :-)
unset IFS
आवश्यक? मैंने सोचा था कि IFS=
एक कमांड को प्रस्तुत करने से केवल उस कमांड में परिवर्तन होता है, बाद में अपने पिछले मूल्य पर अपने आप वापस आ जाता है।
sorted=()
एक कमांड नहीं है, बल्कि एक दूसरा चर असाइनमेंट है।
मूल प्रतिक्रिया:
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
होगा , क्योंकि लाइन-सेपरेटर के रूप में नियमित रूप से नईलाइन्स के बजाय उपयोग करने का कोई विकल्प नहीं है ।readarray
NUL
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -z
यह एक उपयोगी सुधार है, मुझे लगता है कि -z
विकल्प एक GNU सॉर्ट एक्सटेंशन है।
sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z)
:। यह भी आप में काम करता है bash v4 के बजाय bash v3 का उपयोग कर रहा है, क्योंकि bash v3 में रीडअरे उपलब्ध नहीं है।
<
) प्रक्रिया प्रतिस्थापन के साथ संयुक्त है <(...)
। या इसे सहज रूप से रखने के लिए: क्योंकि (printf "bla")
कोई फ़ाइल नहीं है।
यहां एक शुद्ध बैश क्विकॉर्ट कार्यान्वयन है:
#!/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 वर्ण, ग्लोब वर्ण, आदि) हो सकता है।
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
)।
if [ "$i" -lt "$pivot" ]; then
गया था अन्यथा अन्यथा "2" <"10" सही था। मेरा मानना है कि यह POSIX बनाम लेक्सिकोग्राफिक है; या शायद इनलाइन लिंक ।
यदि आपको सरणी तत्वों में विशेष शेल वर्णों को संभालने की आवश्यकता नहीं है:
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
को सॉर्ट करना होगा ।
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@"
और, निश्चित रूप से, सेट कमांड वर्तमान स्थितीय मापदंडों को रीसेट कर देगा, यदि कोई हो।
टीएल; डीआर :
क्रमबद्ध सरणी 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
सॉर्टिंग निष्पादित करता है (शाब्दिक रूप से, डिफ़ॉल्ट रूप से - sort
POSIX कल्पना देखें ):
"${a_in[@]}"
सुरक्षित रूप से सरणी के तत्वों के लिए विस्तारित a_in
रूप में अलग-अलग तर्क , जो कुछ भी वे होते हैं (व्हॉट्सएप सहित)।
printf '%s\n'
फिर प्रत्येक तर्क को प्रिंट करता है - अर्थात, प्रत्येक सरणी तत्व - अपनी लाइन पर, जैसा कि है।
नोट एक के उपयोग (प्रक्रिया प्रतिस्थापन <(...)
) को इनपुट के रूप में क्रमबद्ध उत्पादन प्रदान करने के लिए read
/ readarray
(stdin, करने के लिए पुनर्निर्देशन के माध्यम से <
,) क्योंकि read
/ readarray
में चलाना चाहिए वर्तमान खोल (एक में नहीं चलाया जाना चाहिए subshell उत्पादन चर के लिए) क्रम में a_out
दिखाई दे सकता है वर्तमान शेल (स्क्रिप्ट के शेष भाग में परिभाषित किए जाने वाले चर के लिए)।
sort
एक सरणी चर में आउटपुट पढ़ना :
बैश v4 +: प्रत्येक तत्व ( ) में अनुगामी को शामिल किए बिना, सरणी चर के तत्वों में readarray -t a_out
व्यक्तिगत लाइनों के आउटपुट को पढ़ता है ।sort
a_out
\n
-t
बैश v3: readarray
मौजूद नहीं है, इसलिए read
इसका उपयोग किया जाना चाहिए: सरणी ( ) चर में पढ़ने के लिए
IFS=$'\n' read -d '' -r -a a_out
कहता read
है , पूरे इनपुट को पढ़ रहा है, लाइनों के पार ( ), लेकिन इसे नए तत्वों द्वारा सरणी तत्वों में विभाजित करना ( !) , जो एक शाब्दिक नई रेखा (LF) का उत्पादन करता है । ), एक तथाकथित एएनएसआई सी-उद्धृत स्ट्रिंग है )।
( , एक ऐसा विकल्प, जिसे वस्तुतः हमेशा उपयोग किया जाना चाहिए , वर्णों की अप्रत्याशित हैंडलिंग को अक्षम करता है ।)-a
a_out
-d ''
IFS=$'\n'
$'\n'
-r
read
\
एनोटेट नमूना कोड:
#!/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
परिवर्तन वैश्विक है ।
म्यूनिख से फ्रैंकफर्ट तक की 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
भी शेल भाषा की पहुंच में हैं।
local -n BSORT="$1"
फंक्शन की शुरुआत में। तब आप मायार्रेbubble_sort myarray
को सॉर्ट करने के लिए चला सकते हैं ।
एक और समाधान जो किसी विशेष वर्ण (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 -z
। LC_ALL=C
स्थान की स्वतंत्र रूप से स्थिर सॉर्ट क्रम सुनिश्चित करता है - कभी कभी स्क्रिप्ट के लिए उपयोगी। अगर तुम चाहो तोsort
लोकेल का सम्मान , तो उसे हटा दें।
<()
निर्माण वर्णनकर्ता प्राप्त पैदा की पाइप लाइन से पढ़ने के लिए, और <
के मानक इनपुट पुनर्निर्देशwhile
इसे करने के लिए पाश। यदि आपको पाइप के अंदर मानक इनपुट तक पहुंचने की आवश्यकता है, तो आप एक और विवरणक का उपयोग कर सकते हैं - पाठक के लिए व्यायाम :)।
अब, शुरुआत में वापस। read
निर्मित में रीडायरेक्ट stdin से उत्पादन पढ़ता है। खाली IFS
विभाजन शब्द विभाजन को सेट करना जो यहां अनावश्यक है - परिणामस्वरूप, read
एकल प्रदान किए गए चर के लिए इनपुट की पूरी 'लाइन' पढ़ता है। -r
विकल्प यहां से अवांछित होने के साथ प्रसंस्करण से भी बच जाता है। अंत में, -d ''
रेखा परिसीमन NUL को सेट करता है - जो बताता हैread
शून्य-समाप्त स्ट्रिंग्स को पढ़ना है।
नतीजतन, लूप को प्रत्येक क्रमिक शून्य-समाप्त सरणी तत्व के लिए एक बार निष्पादित किया जाता है, जिसमें मूल्य को संग्रहीत किया जाता है e
। उदाहरण सिर्फ वस्तुओं को दूसरे सरणी में रखता है, लेकिन आप उन्हें सीधे संसाधित करना पसंद कर सकते हैं :)।
बेशक, यह एक ही लक्ष्य को प्राप्त करने के कई तरीकों में से एक है। जैसा कि मैं इसे देखता हूं, यह पूरी तरह से छंटाई में एल्गोरिथ्म को लागू करने की तुलना में सरल है और कुछ मामलों में यह तेज होगा। यह newlines सहित सभी विशेष पात्रों को संभालता है और अधिकांश सामान्य प्रणालियों पर काम करना चाहिए। सबसे महत्वपूर्ण बात, यह आपको बैश के बारे में कुछ नया और भयानक सिखा सकता है :)।
e
करने और खाली IFS को सेट करने के बजाय , REPLY वैरिएबल का उपयोग करें।
यदि आप सरणी में प्रत्येक तत्व के लिए एक अद्वितीय पूर्णांक की गणना कर सकते हैं, जैसे:
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[@]}"
न्यूनतम सॉर्ट:
#!/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
रिक्त स्थान और 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 सेट करते हैं$*
।[[ $* =~ [$wa] ]]
।exit 1
set -f
IFS=$'\n'
) एक लूप वेरिएबल x
और एक नया संस्करण ( ) सेट करें nl=$'\n'
।$@
) के सभी मूल्यों को प्रिंट करते हैं ।"${@//$nl/$wa}"
।sort -n
।set --
।for x
sorted+=(…)
"${x//$wa/$nl}"
।यह प्रश्न निकट से संबंधित है। और बीटीडब्ल्यू, यहां बैश में एक मर्ज है (बाहरी प्रक्रियाओं के बिना):
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[@]}"
मुझे यकीन नहीं है कि आपको बैश में एक बाहरी छँटाई कार्यक्रम की आवश्यकता होगी।
यहाँ सरल बुलबुला-सॉर्ट एल्गोरिथ्म के लिए मेरा कार्यान्वयन है।
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))
अंतिम दर्जन तत्वों तक का उपयोग करते हैं। अंतिम तत्वों के लिए, चयन प्रकार का उपयोग किया जाता है।
a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
बैश / लिनेक्स की भावना में, मैं प्रत्येक चरण के लिए सर्वश्रेष्ठ कमांड-लाइन टूल को पाइप करूंगा। sort
मुख्य काम करता है, लेकिन अंतरिक्ष के बजाय न्यूलाइन द्वारा अलग किए गए इनपुट की आवश्यकता होती है, इसलिए बहुत सरल पाइपलाइन ऊपर बस करता है:
इको सरणी सामग्री -> स्थान को न्यूलाइन द्वारा बदलें -> सॉर्ट करें
$()
परिणाम को प्रतिध्वनित करना है
($())
एक सरणी में "गूंज परिणाम" डाल दिया है
नोट : जैसा कि @sorontar ने एक अलग प्रश्न के लिए एक टिप्पणी में उल्लेख किया है :
सॉर्ट किया गया = ($ (...)) भाग "विभाजन और ग्लोब" ऑपरेटर का उपयोग कर रहा है। आपको ग्लोब को बंद कर देना चाहिए: सेट -f या सेट -o noglob या shopt -op noglob या एरे का एक तत्व जैसे * फ़ाइलों की सूची में विस्तारित किया जाएगा।
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[@]}"
। अन्य एंटीपैटर्न है: ($())
एक सरणी में "प्रतिध्वनि परिणाम" डालना । हरगिज नहीं! यह एक भयानक एंटीपैटर्न है जो पाथनाम विस्तार (ग्लोबिंग) और शब्द विभाजन के कारण टूट जाता है। कभी भी इस डरावनी का उपयोग न करें।
IFS
, यह आपके तत्वों को छोटे टुकड़ों में विभाजित कर देगा यदि उनमें व्हाट्सएप है। लोप के साथ उदाहरण की कोशिश करोIFS=$'\n'
और देखो!