Відповіді:
Вам не дуже потрібен весь стільки код:
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
Підтримує пробіли в елементах (доки це не новий рядок) і працює в Bash 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
Зверніть увагу, що ця версія справляється зі значеннями, що містять спеціальні символи або пробіли ( крім нових рядків)
Примітка readarray підтримується в Баш 4+.
Редагувати На підставі пропозиції @Dimitre я оновив її до:
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
що має перевагу навіть розуміти елементи сортування із символами нового рядка. На жаль, як правильно сигналізував @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 v3 замість bash v4, оскільки readhray недоступний у bash v3.
<
) у поєднанні з заміною процесу <(...)
. Або кажучи інтуїтивно: бо (printf "bla")
це не файл.
Ось чиста реалізація Bash quicksort:
#!/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
щоб файли в поточній папці були відсортовані за часом модифікації (найновіший перший).
ПРИМІТКА. Ці функції чисті Bash! жодних зовнішніх комунальних служб і жодних передплаток! вони є безпечними для всіх смішних символів (пробіли, символи нового рядка, символи глобуса тощо).
sort
пропонує, достатньо, рішення sort
+ read -a
буде швидше, починаючи з, скажімо, 20 предметів, і все більше і значно швидше, чим більше елементів, з якими ви маєте справу. Наприклад, на моєму кінці 2012 року iMac під керуванням OSX 10.11.1 із Fusion Drive: 100-елементний масив: ca. 0,03 сек. ( qsort()
) проти ca. 0,005 сек. ( sort
+ read -a
); Масив 1000 елементів: ca. 0,375 сек. ( qsort()
) проти ca. 0,014 сек ( sort
+ read -a
).
if [ "$i" -lt "$pivot" ]; then
потрібно було, інакше вирішене значення "2" <"10" повернене "true". Я вважаю, що це POSIX проти лексикографічного; або, можливо, Inline Link .
Якщо вам не потрібно обробляти спеціальні символи оболонки в елементах масиву:
array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))
З bash вам все одно знадобиться програма зовнішнього сортування.
З zsh не потрібні зовнішні програми, і спеціальні символи оболонки легко обробляються:
% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}"
3
5
a a
b
c
f
ksh має set -s
сортувати ASCIIbetically .
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@"
І, звичайно, команда set буде скинути поточні позиційні параметри, якщо такі є.
tl; dr :
Сортувати масив a_in
і зберегти результат у a_out
(елементи не повинні мати вбудовані нові рядки [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)
Переваги перед рішенням антака :
Вам не потрібно турбуватися про випадкове глобування (випадкова інтерпретація елементів масиву як шаблонів імен файлів), тому додаткова команда не потрібна для відключення глобулінгу ( set -f
та set +f
відновлення його пізніше).
Вам не потрібно турбуватися про скидання IFS
з unset IFS
. [2]
Вище комбінати Bash коду із зовнішнім утилітою sort
для вирішення , яке працює з довільними окремими -LINE елементів і або лексичної або числовий сортуванням (необов'язково по полю) :
Продуктивність : Приблизно 20 елементів і більше , це буде швидше, ніж чисте рішення Bash - значно і все частіше, коли ви перейдете понад 100 елементів.
(Точні пороги залежать від конкретного входу, машини та платформи.)
printf '%s\n' "${a_in[@]}" | sort
виконує сортування (лексично, за замовчуванням - див sort
. специфікацію POSIX ):
"${a_in[@]}"
безпечно розширюється на елементи масиву a_in
як окремі аргументи , що б вони не містили (включаючи пробіли).
printf '%s\n'
потім друкує кожен аргумент - тобто кожен елемент масиву - у власному рядку, як є.
Зауважте використання підстановки процесу ( <(...)
) для надання відсортованого виводу як вхід до read
/ readarray
(через перенаправлення на stdin, <
), оскільки read
/ readarray
повинно працювати в поточній оболонці (не повинно запускатися в нижній частині ) для того, щоб вихідна змінна a_out
була видно до поточної оболонки (щоб змінна залишалася визначеною в решті сценарію).
Виведення читання sort
у змінну масиву :
Bash v4 +: readarray -t a_out
зчитує окремі рядки, що виводяться sort
в елементи змінної масиву a_out
, не включаючи трейлінг \n
у кожному елементі ( -t
).
Bash v3: readarray
не існує, тому read
його слід використовувати:
IFS=$'\n' read -d '' -r -a a_out
каже read
читати в -a
змінну array ( ) a_out
, читаючи весь вхід, по рядках ( -d ''
), але розділяючи її на елементи масиву на нові рядки ( IFS=$'\n'
. $'\n'
, Що створює буквальний новий рядок (LF ), - це так звана ANSI C-цитується рядок ).
( -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
[1] для обробки елементів з вбудованими, переклади рядків використовувати наступний варіант (Bash V4 +, з ГНУ sort
):
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z)
.
Корисна відповідь Міхала Горни має рішення Bash v3.
[2] У той час як IFS
це встановлено у варіанті v3 Bash, зміна області видимості команди .
Навпаки, IFS=$'\n'
у відповіді антака випливає завдання, а не команда, і в цьому випадку IFS
зміна є глобальною .
У тригодинній поїздці поїздом з Мюнхена до Франкфурта (до якої у мене виникли труднощі, оскільки Октоберфест починається завтра) я думав про своє перше повідомлення. Використання глобального масиву набагато краща ідея для загальної функції сортування. Наступна функція обробляє довільні рядки (нові рядки, пробіли тощо):
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[@]}
Зауважте, що, ймовірно, Bash використовує внутрішньо смарт-покажчики, тому операція підкачки може бути дешевою (хоча я сумніваюся в цьому). Однак, bubble_sort
демонструє, що більш досконалі функції, подібні merge_sort
також, знаходяться в межах досяжності мови оболонки.
local -n BSORT="$1"
на початку функції. Тоді ви можете бігти, bubble_sort myarray
щоб сортувати міар .
Ще одне рішення, яке використовує зовнішні sort
та справляється з будь-якими спеціальними символами (крім NULs :)). Має працювати з bash-3.2 та GNU або BSD sort
(на жаль, POSIX не включає -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
Вбудований зчитує вихід з перенаправлені стандартного введення. Встановлення порожнього IFS
відключення розділення слів, яке тут непотрібне - як результат, read
зчитується весь "рядок" введення до єдиної наданої змінної. -r
опція вимикає обробку евакуації, яка тут також небажана. Нарешті, -d ''
встановлює розмежувач рядків на NUL - тобто повідомляє read
читати рядки з нульовим завершенням.
В результаті цикл виконується один раз для кожного наступного елемента з масивом, що закінчується нулем, при цьому значення зберігається в e
. У прикладі просто розміщуються елементи в іншому масиві, але ви можете віддати перевагу їх безпосередньо :).
Звичайно, це лише один із багатьох способів досягнення тієї ж мети. Як я бачу, це простіше, ніж реалізація повного алгоритму сортування в bash, а в деяких випадках це буде швидше. Він обробляє всі спеціальні символи, включаючи нові рядки, і повинен працювати над більшістю загальних систем. Найголовніше, що це може навчити вас чомусь новому і дивовижному про баш :).
e
та встановлення порожнього IFS використовуйте змінну REPLY.
спробуйте це:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
Вихід буде:
3 5 а б c f
Проблема вирішена.
Якщо ви можете обчислити унікальне ціле число для кожного елемента в масиві, наприклад:
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 завжди використовує розріджений масив, тому не потрібно турбуватися про невикористані індекси:
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
Існує рішення для звичайної проблеми пробілів та нових рядків:
Використовуйте символ , який не в вихідному масиві (наприклад , $'\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
та нового рядка var ( nl=$'\n'
).$@
)."${@//$nl/$wa}"
.sort -n
.set --
.for x
sorted+=(…)
"${x//$wa/$nl}"
.Це питання виглядає тісно пов’язаним. І BTW, ось об'єднання в Bash (без зовнішніх процесів):
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.
Ось моя реалізація для простого алгоритму сортування бульбашок.
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))
У дусі bash / linux я б передав найкращий інструмент командного рядка для кожного кроку. sort
виконує основну роботу, але потребує введення, розділеного новою лінією замість місця, тому дуже простий конвеєр вище просто робить:
Вміст масиву Echo -> замініть простір на новий рядок -> сортувати
$()
це перегукувати результат
($())
полягає в тому, щоб помістити "відлунений результат" у масив
Примітка . Як згадував @sorontar у коментарі до іншого питання:
У відсортованій = ($ (...)) частині використовується оператор "спліт і глоб". Вам слід вимкнути глобус: встановити -f або set -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'
пропущеним і дивіться!