Ответы:
Вам не нужно много кода:
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 указал, что необходимо соблюдать осторожность, если элементы содержат символы подстановки, такие как *
или ?
:
Часть sorted = ($ (...)) использует оператор «split and glob». Вы должны отключить glob:
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 поддерживается в bash 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, потому что readarray недоступен в bash v3.
<
) в сочетании с заменой процесса <(...)
. Или, если выразиться интуитивно: потому что (printf "bla")
это не файл.
Вот чистая реализация быстрой сортировки Bash:
#!/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
}
В обоих случаях вы можете изменить используемый порядок: я использовал сравнение строк, но вы можете использовать арифметические сравнения, сравнивать время изменения файла и т. Д., Просто используйте соответствующий тест; Вы можете даже сделать его более общим и использовать его в качестве первого аргумента, который используется тестовой функцией, например:
#!/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 элементов: ок. 0,03 с. ( qsort()
) против ок. 0,005 с. ( sort
+ read -a
); Массив из 1000 элементов: ок. 0,375 с. ( qsort()
) против ок. 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 сбросит текущие позиционные параметры, если таковые имеются.
тл; др :
Сортируйте массив 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)
Преимущества перед решением Antak :
Вам не нужно беспокоиться о случайном смещении (случайная интерпретация элементов массива как шаблона имени файла), поэтому не требуется дополнительная команда, чтобы отключить сглаживание ( set -f
и set +f
восстановить его позже).
Вам не нужно беспокоиться о сбросе IFS
с unset IFS
. [2]
Вышеприведенное объединяет код Bash с внешней утилитой sort
для решения, которое работает с произвольными однострочными элементами и лексической или числовой сортировкой (необязательно по полю) :
Производительность. Приблизительно для 20 или более элементов это будет быстрее, чем для простого решения Bash, причем значительно и все больше, когда вы преодолеете около 100 элементов.
(Точные пороговые значения будут зависеть от вашего конкретного входа, машины и платформы.)
printf '%s\n' "${a_in[@]}" | sort
выполняет сортировку (по умолчанию лексически - см. sort
спецификацию POSIX ):
"${a_in[@]}"
безопасно расширяется до элементов массива в a_in
качестве отдельных аргументов , что бы они ни содержали (включая пробелы).
printf '%s\n'
затем печатает каждый аргумент, т. е. каждый элемент массива, в отдельной строке, как есть.
Обратите внимание на использование процесса substitution ( <(...)
) для предоставления отсортированного вывода в качестве входных данных для 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
изменение является глобальным .
В 3-часовой поездке на поезде из Мюнхена во Франкфурт (до которой мне было трудно добраться, потому что завтра начинается Октоберфест), я думал о своем первом посте. Использование глобального массива - гораздо лучшая идея для общей функции сортировки. Следующая функция обрабатывает произвольные строки (переводы строк, пробелы и т. Д.):
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 внутренне использует smart-указатели, поэтому операция подкачки может быть дешевой (хотя я сомневаюсь в этом). Тем не менее, bubble_sort
демонстрирует, что более продвинутые функции, такие merge_sort
как также доступны в языке оболочки.
local -n BSORT="$1"
в начале функции. Тогда вы можете запустить, bubble_sort myarray
чтобы отсортировать myarray .
Другое решение, которое использует внешние sort
и справляется с любыми специальными символами (кроме NUL :)). Должен работать с 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
опция отключает обработку escape, которая также нежелательна Наконец, -d ''
устанавливает разделитель строки в NUL - то есть говорит read
читать строки с нулем в конце .
В результате цикл выполняется один раз для каждого последующего элемента массива с нулевым символом в конце, причем значение сохраняется в e
. В примере просто помещаются элементы в другой массив, но вы можете предпочесть обрабатывать их напрямую :).
Конечно, это только один из многих способов достижения одной и той же цели. На мой взгляд, это проще, чем реализовать полный алгоритм сортировки в bash, а в некоторых случаях это будет быстрее. Он обрабатывает все специальные символы, включая символы новой строки, и должен работать в большинстве распространенных систем. Самое главное, он может научить вас чему-то новому и удивительному в bash :).
e
и устанавливать пустой IFS, используйте переменную REPLY.
попробуй это:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
Выход будет:
3 5 б с е
Задача решена.
Если вы можете вычислить уникальное целое число для каждого элемента в массиве, например так:
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}"
.Этот вопрос выглядит тесно связанным. И кстати, вот слияние в 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
выполняет основную работу, но требует ввода, разделенного новой строкой, а не пробелом, поэтому очень простой конвейер выше просто делает:
Содержимое массива эха -> заменить пробел новой строкой -> сортировать
$()
это повторить результат
($())
это положить "отраженный результат" в массиве
Примечание : как @sorontar упоминается в комментарии к другому вопросу:
Часть sorted = ($ (...)) использует оператор «split and glob». Вы должны отключить glob: set -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'
опущенным и посмотрите!