Yanıtlar:
Gerçekten bu kadar koda ihtiyacınız yok:
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
Elemanlardaki boşlukları destekler (yeni satır olmadığı sürece) ve Bash 3.x'te çalışır.
Örneğin:
$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]
Not: @sorontar olan işaret elemanları gibi genel arama karakterleri içeriyorsa bakımı gerekli olduğu *ya da ?:
Sort = ($ (...)) bölümü "split and glob" operatörünü kullanıyor. Glob'u kapatmalısınız:
set -fveyaset -o noglobveyashopt -op noglobveya dizinin bir öğesi*bir dosya listesine genişletilecektir.
Sonuç, bu sırada gerçekleşen altı şeyin doruk noktasıdır:
IFS=$'\n'"${array[*]}"<<<sortsorted=($(...))unset IFSIFS=$'\n'Bu, operasyonumuzun 2 ve 5'in sonuçlarını aşağıdaki şekilde etkileyen önemli bir parçasıdır:
Verilen:
"${array[*]}" ilk karakteri ile sınırlanan her öğeye genişler IFSsorted=() her karakterine bölünerek elemanlar oluşturur IFSIFS=$'\n' öğeleri yeni bir satır kullanarak genişletilecek şekilde ayarlar , sınırlayıcı olarak ve daha sonra her satır bir öğe olacak şekilde oluşturulacak şekilde . (örneğin, yeni bir satıra bölme.)
Yeni bir satırla sınırlamak önemlidir, çünkü bu şekilde sortçalışır (satır başına sıralama). Yalnızca yeni bir satıra bölmek o kadar önemli değildir, ancak boşluk veya sekme içeren öğeleri korumak gerekir.
Varsayılan değeri IFSolan bir boşluk , bir sekme ardından yeni bir satır ve bizim operasyon için elverişsiz olacaktır.
sort <<<"${array[*]}"bölüm<<<, burada dizeler olarak adlandırılır , "${array[*]}"yukarıda açıklandığı gibi genişlemesini alır ve standart girdiyesort .
Örneğimizle, sortşu dizeyle beslenir:
a c
b
f
3 5
sort Çeşitlerinden beri üretir:
3 5
a c
b
f
sorted=($(...))bölüm$(...)Denilen kısmı, komut ikamesi , içeriğini (neden sort <<<"${array[*]}sonuçlanan çekerken, normal bir komut olarak çalıştırmak için) standart çıktıyı nereye kadar gider literal olarak $(...)oldu.
Örneğimizde, bu sadece yazmaya benzer bir şey üretir:
sorted=(3 5
a c
b
f
)
sorted daha sonra bu değişmez değeri her yeni satıra bölerek oluşturulan bir dizi haline gelir.
unset IFSBu, değerini IFSvarsayılan değerine sıfırlar ve sadece iyi bir uygulamadır.
Daha IFSsonra senaryomuzda güvenilen hiçbir şeyle ilgili sorun yaratmamamızı sağlamaktır . (Aksi takdirde, bazı şeyleri değiştirdiğimizi hatırlamamız gerekir - karmaşık komut dosyaları için pratik olmayan bir şey.)
IFSsadece bir tür boşluk varsa, öğelerinizi küçük parçalara ayırır. İyi; mükemmel değil :-)
unset IFSgerekli? IFS=Bir komuta geçmenin , sadece bu komuta geçişi kapsadığını , daha sonra otomatik olarak önceki değerine döndüğünü düşündüm .
sorted=()bir komut değil, ikinci bir değişken ataması.
Orijinal yanıt:
array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)
çıktı:
$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f
Bu sürümün özel karakterler veya boşluk içeren değerlerle ( yeni satırlar hariç ) başa çıktığını unutmayın
Not readarray, bash 4 ve üstü sürümlerde desteklenir.
Düzenle @Dimitre tarafından yapılan öneriye dayanarak şu şekilde güncellemiştim:
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
hatta doğru yerleştirilmiş yeni satır karakterleri ile sıralama öğeleri anlamak yararına sahiptir . Ne yazık ki, @ruakh tarafından doğru bir şekilde işaret edildiği gibi, bu, sonucun doğrureadarray olacağı anlamına gelmedi , çünkü satır ayırıcılar olarak normal satır satırları yerine kullanma seçeneği yoktur .readarrayNUL
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -zyararlı bir gelişme, -zseçenek bir GNU sıralama uzantısı olduğunu varsayalım .
sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z). Bu, bash v4 yerine bash v3 kullandığınız için de geçerlidir, çünkü bash v3'te readarray kullanılamaz.
< ile birlikte girdi yeniden yönlendirmesi ( ) . Ya da sezgisel olarak koymak: çünkü bir dosya değil. <(...)(printf "bla")
İşte saf bir Bash quicksort uygulaması:
#!/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[@]}" )
}
Örneğin,
$ 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")'
Bu uygulama özyinelemeli… yani yinelemeli bir hızlı sıralama:
#!/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
}
Her iki durumda da, kullandığınız sırayı değiştirebilirsiniz: Dize karşılaştırmaları kullandım, ancak aritmetik karşılaştırmaları kullanabilir, wrt dosyası değiştirme zamanını karşılaştırabilir, vb. daha genel hale getirebilir ve test işlevinin kullanıldığı ilk argümanı kullanmasını sağlayabilirsiniz, ör.
#!/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
}
Sonra bu karşılaştırma fonksiyonuna sahip olabilirsiniz:
compare_mtime() { [[ $1 -nt $2 ]]; }
ve kullan:
$ qsort compare_mtime *
$ declare -p qsort_ret
geçerli klasördeki dosyaların değişiklik zamanına göre sıralanması (önce en yenisi).
NOT. Bu işlevler saf Bash! harici yardımcı programlar ve alt kabuklar yok! komik semboller (boşluklar, yeni satır karakterleri, glob karakterleri vb.)
sortyeterliyse, bir sort+ read -açözümü, örneğin 20 öğeden başlayarak daha hızlı ve daha fazla uğraştığınız unsurları giderek ve önemli ölçüde daha hızlı olacaktır. Örneğin, 2012'nin sonlarında bir Fusion Drive ile OSX 10.11.1 çalıştıran iMac'imde: 100 elemanlı dizi: ca. 0.03s saniye. ( qsort()) ve ca. 0.005 saniye. ( sort+ read -a); 1000 elemanlık dizi: ca. 0.375 saniye. ( qsort()) ve ca. 0.014 saniye ( sort+ read -a).
if [ "$i" -lt "$pivot" ]; thengerekliydi, aksi takdirde çözülen "2" <"10" true değerini döndürdü. Bunun POSIX ve Sözlükbilimsel olduğuna inanıyorum; veya belki Inline Link .
Dizi öğelerinde özel kabuk karakterlerini işlemeniz gerekmiyorsa:
array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))
Bash ile zaten harici bir sıralama programına ihtiyacınız olacak.
Zsh ile harici programlara gerek yoktur ve özel kabuk karakterleri kolayca ele alınır:
% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}"
3
5
a a
b
c
f
Ksh vardır set -ssıralamak için ASCIIbetically .
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@" Ve elbette, set komutu, varsa, geçerli konum parametrelerini sıfırlar.
tl; dr :
Diziyi sıralayın a_inve sonucu saklayın a_out(öğelerin gömülü yeni satırları olmamalıdır [1]
):
Bash v4 +:
readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Bash v3:
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Antak'ın çözümüne göre avantajları :
Yanlışlıkla globbing (dizi öğelerinin dosya adı kalıpları olarak yanlışlıkla yorumlanması) konusunda endişelenmenize gerek yoktur, bu nedenle globbing'i devre dışı bırakmak ( set -fve set +fdaha sonra geri yüklemek için) için fazladan komut gerekmez .
Sen sıfırlama hakkında endişe gerekmez IFSile unset IFS. [2]
Yukarıdaki, rastgele tek satırlı öğelerle ve sözcüksel veya sayısal sıralama (isteğe bağlı olarak alana göre) ile çalışansort bir çözüm için Bash kodunu harici yardımcı programla birleştirir :
Performans : Yaklaşık 20 veya daha fazla element için , bu saf Bash çözümünden daha hızlı olacaktır - yaklaşık 100 elementin ötesine geçtiğinizde önemli ölçüde ve giderek daha fazla.
(Tam eşikler, giriş, makine ve platformunuza bağlı olacaktır.)
printf '%s\n' "${a_in[@]}" | sort sıralama yapar (sözlük olarak varsayılan olarak - sortPOSIX spesifikasyonuna bakın ):
"${a_in[@]}"dizinin öğelerine, içerdikleri her ne olursa olsun (boşluk dahil) bağımsız bir şekilde bağımsıza_in olarak genişler .
printf '%s\n' sonra her bir argümanı - yani her dizi elemanını - kendi satırına olduğu gibi yazdırır.
Not bir kullanımını işlemi ikame ( <(...)) bir giriş olarak sıralanmış çıkışı sağlamak için read/ ' readarray(stdin, yeniden yönlendirme ile <, çünkü) read/ readarrayçalışmalıdır akım kabuk (a çalıştırmak gerekir alt kabuğa çıkış değişkeni için sırayla) a_outgörünür olmasını geçerli kabuğa (değişkenin kodun geri kalanında tanımlanmış olarak kalması için).
Okuma sort çıktısını dizi değişkenine çevirir :
Bash v4 +: readarray -t a_outayrı ayrı satırları okursort her bir öğenin ( ) a_outsonuna iz bırakmadan , dizi değişkeninin öğelerine .\n-t
Bash v3: readarraymevcut değil, bu yüzden readkullanılmalıdır: dizi ( ) değişkenine okumayı, tüm girdiyi satırlar ( ) arasında okumayı , ancak yeni satır ((LF) ile satır öğelerine ayırmayı
IFS=$'\n' read -d '' -r -a a_outsöyler . ), ANSI C alıntılı bir dizedir ).
( , neredeyse her zaman birlikte kullanılması gereken bir seçenekread-aa_out-d ''IFS=$'\n'$'\n'-rread , \karakterlerin beklenmedik şekilde işlenmesini devre dışı bırakır .)
Açıklamalı örnek kod:
#!/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[@]}"
sortSeçeneklerin kullanılmamasından dolayı , bu, sözcüksel sıralama sağlar (rakamlar harflerden önce sıralanır ve basamak dizileri sayı olarak değil, sözcük olarak ele alınır):
*
10
5
a c
b
f
Eğer isteseydik sayısal 1 alana göre sıralama, kullanmak istiyorum sort -k1,1nyerine sadece sorthangi verimleri (bir çeşit sayılar önce olmayan numaralar ve sayılar doğru sıralama),:
*
a c
b
f
5
10
[1] gömülü yeni satır ile etmek için sap elemanları, aşağıdaki varyantı ile (Bash V4 +, kullanımı GNU'da sort )
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z).
Michał Górny'nin yardımcı cevabı Bash v3 çözümüne sahip.
, [2] IFS bir Bash v3 varyantı yer değiştirmek olan komuta kapsama .
Buna karşılık, ne şu IFS=$'\n' antak yanıtında bir olan atama bu durumda bir komutla, yerine IFSdeğişimdir küresel .
Münih'ten Frankfurt'a giden 3 saatlik tren gezisinde (Oktoberfest yarın başlayacağı için ulaşmakta sorun yaşadım) ilk görevimi düşünüyordum. Genel bir dizi kullanmak, genel bir sıralama işlevi için çok daha iyi bir fikirdir. Aşağıdaki işlev, arbiter dizeleri (satırsonları, boşluklar vb.) İşler:
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[@]}
Bu yazdırır:
3 5 a b c z y
Aynı çıktı,
BSORT=(a c b 'z y' 3 5)
bubble_sort
echo ${BSORT[@]}
Muhtemelen Bash'in dahili olarak akıllı işaretçiler kullandığını unutmayın, bu yüzden takas işlemi ucuz olabilir (buna rağmen şüpheliyim). Bununla birlikte, bubble_sortdaha gelişmiş fonksiyonların merge_sortda kabuk dilinin erişiminde olduğunu gösterir.
local -n BSORT="$1"işlevin başlangıcında. Sonra myarraybubble_sort myarray sıralamak için koşabilirsiniz .
Harici kullanan sortve herhangi bir özel karakterle başa çıkan başka bir çözüm (NUL'lar hariç :)). Bash-3.2 ve GNU veya BSD ile çalışmalıdır sort(ne yazık ki POSIX içermez -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)
Önce sondaki giriş yönlendirmesine bakın. printfSıfır sonlandırılmış dizi öğelerini yazmak için yerleşik kullanıyoruz . Alıntı, dizi öğelerinin olduğu gibi iletildiğinden emin printfolur ve kabuk özellikleri, kalan her parametre için biçim dizesinin son bölümünü yeniden kullanmasına neden olur. Yani, şuna benzer:
for e in "${array[@]}"; do
printf "%s\0" "${e}"
done
Sonra null sonlandırılmış öğe listesi iletilir sort. Bu -zseçenek, boş sonlandırılmış öğeleri okumasına, sıralamasına ve boş sonlandırılmış çıkış vermesine neden olur. Sadece benzersiz öğeleri elde etmeniz gerekiyorsa -u, daha taşınabilir olduğu için geçebilirsiniz uniq -z. LC_ALL=CYereli bağımsız kararlı sıralama düzeni sağlar - komut dosyaları için bazen yararlı. sortYerel ayara saygı duymak istiyorsanız, bunu kaldırın.
<()Yapı olurken boru okuma tanımlayıcıyı alır ve <standart giriş yönlendirmeleriwhile buna döngü. Borunun içindeki standart girişe erişmeniz gerekiyorsa, başka bir tanımlayıcı kullanabilirsiniz - okuyucu için egzersiz :).
Şimdi, başlangıca geri dönelim. readYerleşik Yönlendirilen stdin'den çıkışını okur. Boş ayar yapmak IFS, burada gereksiz olan kelime bölmeyi devre dışı bırakır - sonuç olarak, readsağlanan tek 'değişken' satırını sağlanan tek değişkene okur. -rseçeneği, burada istenmeyen kaçış işlemini de devre dışı bırakır. Son olarak, -d ''çizgi sınırlayıcıyı NUL olarak ayarlar - yani,read sıfır sonlu dizeleri okumayı .
Sonuç olarak, döngü her ardışık sıfır sonlandırılmış dizi öğesi için bir kez yürütülür ve değer depolanır e. Örnek sadece öğeleri başka bir diziye koyar, ancak doğrudan işlemeyi tercih edebilirsiniz :).
Tabii ki, bu aynı hedefe ulaşmanın birçok yolundan sadece biri. Gördüğüm gibi, bash'da tam sıralama algoritması uygulamaktan daha basit ve bazı durumlarda daha hızlı olacak. Yeni satırlar da dahil olmak üzere tüm özel karakterleri işler ve ortak sistemlerin çoğunda çalışmalıdır. En önemlisi, size bash hakkında yeni ve harika bir şey öğretebilir :).
eve boş IFS ayarlamak yerine REPLY değişkenini kullanın.
bunu dene:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
Çıktı:
3 5 bir b c f
Sorun çözüldü.
Dizideki her öğe için benzersiz bir tamsayı hesaplayabiliyorsanız, şöyle:
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 -> $?"
Bash her zaman seyrek dizi kullandığından, bu tam sayıları dizi dizinleri olarak kullanabilirsiniz, bu nedenle kullanılmayan dizinler hakkında endişelenmenize gerek yoktur:
array=(a c b f 3 5)
for el in "${array[@]}"; do
sexy_int "$el"
sorted[$?]="$el"
done
echo "${sorted[@]}"
dk. sıralama:
#!/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
Alanların ve yeni satırların olağan sorunu için bir çözüm var:
Orijinal dizideki (gibi olmayan bir karakter kullanarak $'\1'veya $'\4'veya benzeri).
Bu işlev işi yapar:
# 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
}
Bu diziyi sıralar:
$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>
Bu, kaynak dizinin geçici karakter içerdiğinden şikayet edecektir:
$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char
wa(geçici çözüm karakter) ve boş bir IFS ayarladık$*.[[ $* =~ [$wa] ]].exit 1set -fIFS=$'\n'Bir döngü değişkeni xve bir yeni satır var ( nl=$'\n') yeni bir IFS ( ) değeri ayarlayın .$@) tüm değerlerini yazdırıyoruz ."${@//$nl/$wa}".sort -n.set --.for xsorted+=(…)"${x//$wa/$nl}".Bu soru birbiriyle yakından ilgili. Ve BTW, işte Bash'te bir birleşim (harici işlemler olmadan):
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[@]}"
Bash'te harici bir sıralama programına ihtiyacınız olacağından emin değilim.
İşte basit kabarcık sıralama algoritması için benim uygulama.
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[@]})"
Bu aşağıdakileri basacaktır:
input: a c b f 3 5
output: 3 5 a b c f
O(n^2). Çoğu sıralama algoritmalarının O(n lg(n))son düzine öğeye kadar kullandığını hatırlıyorum . Son elemanlar için seçim sıralaması kullanılır.
a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
Bash / linux ruhu içinde, her adım için en iyi komut satırı aracını kullanırdım. sortana işi yapar ancak boşluk yerine satırsonu ile ayrılması gerekir, bu nedenle yukarıdaki basit boru hattı basitçe şunları yapar:
Yankı dizisi içeriği -> alanı satırsonu yerine değiştir -> sırala
$() sonucu yankılamak
($()) "echoed sonucunu" bir diziye koymak
Not : @sorontar bir belirtildiği gibi comment farklı bir soruya:
Sort = ($ (...)) bölümü "split and glob" operatörünü kullanıyor. Glob'u kapatmalısınız: set -f veya set -o noglob veya shopt -op noglob veya * gibi dizinin bir öğesi bir dosya listesine genişletilir.
mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort)aksi takdirde sorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort).
echo ${array[@]} | tr " " "\n"dizi alanları beyaz boşluklar ve glob karakterleri içeriyorsa kırılır. Ayrıca, bir alt kabuk ortaya çıkarır ve işe yaramaz bir dış komut kullanır. Ve nedeniyle echodilsiz olmak, bu dizi ile başlıyorsa kıracak -e, -Eya da -n. Bunun yerine kullanın: printf '%s\n' "${array[@]}". Diğer antipattern: ($())"yankılanan sonucu" bir diziye koymaktır . Kesinlikle değil! Bu, yol adı genişlemesi (globbing) ve kelime bölünmesi nedeniyle kırılan korkunç bir antipattern. Asla bu dehşeti kullanmayın.
IFSöğelerinizde boşluklar varsa öğelerinizi küçük parçalara böler. ÖrneğinIFS=$'\n'atlanmış ile deneyin ve görün!