Вам не нужно много кода:
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
, он разделит ваши элементы на маленькие кусочки, если в них есть пробелы. Попробуйте, например, сIFS=$'\n'
опущенным и посмотрите!IFS
, он разбивает ваши элементы на маленькие кусочки, если в них есть только один конкретный вид пробелов. Хорошо; не идеально :-)unset IFS
необходимым? Я думал, что добавлениеIFS=
к команде ограничивает изменение только этой команды, автоматически возвращаясь к ее предыдущему значению.sorted=()
это не команда, а назначение второй переменной.Оригинальный ответ:
вывод:
Обратите внимание, что эта версия справляется со значениями, которые содержат специальные символы или пробелы ( кроме новых строк)
Примечание readarray поддерживается в bash 4+.
Редактировать На основании предложения @Dimitre я обновил его до:
который имеет преимущество даже понимания элементов сортировки с символами новой строки, вставленными правильно. К сожалению, как правильно сообщается @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:
Используйте как, например,
Эта реализация является рекурсивной ... так вот итеративная быстрая сортировка:
В обоих случаях вы можете изменить используемый порядок: я использовал сравнение строк, но вы можете использовать арифметические сравнения, сравнивать время изменения файла и т. Д., Просто используйте соответствующий тест; Вы можете даже сделать его более общим и использовать его в качестве первого аргумента, который используется тестовой функцией, например:
Тогда вы можете иметь эту функцию сравнения:
и использовать:
чтобы файлы в текущей папке были отсортированы по времени изменения (сначала самые новые).
НОТА. Эти функции являются чисто 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 .Если вам не нужно обрабатывать специальные символы оболочки в элементах массива:
С bash вам все равно понадобится внешняя программа сортировки.
С zsh внешние программы не требуются, а специальные символы оболочки легко обрабатываются:
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 +:
Bash v3:
Преимущества перед решением 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
, отключает неожиданную обработку\
символов.)Аннотированный пример кода:
Из-за использования
sort
параметров без, это приводит к лексической сортировке (цифры сортируются перед буквами, а последовательности цифр обрабатываются лексически, а не как числа):Если бы вы хотели числовую сортировку по 1-му полю, вы бы использовали
sort -k1,1n
вместо простоsort
, что приводит к (сортировка не по числам перед числами, а сортировка по номерам правильно):[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-часовой поездке на поезде из Мюнхена во Франкфурт (до которой мне было трудно добраться, потому что завтра начинается Октоберфест), я думал о своем первом посте. Использование глобального массива - гораздо лучшая идея для общей функции сортировки. Следующая функция обрабатывает произвольные строки (переводы строк, пробелы и т. Д.):
Это печатает:
Тот же вывод создается из
Обратите внимание, что, вероятно, Bash внутренне использует smart-указатели, поэтому операция подкачки может быть дешевой (хотя я сомневаюсь в этом). Тем не менее,
bubble_sort
демонстрирует, что более продвинутые функции, такиеmerge_sort
как также доступны в языке оболочки.источник
local -n BSORT="$1"
в начале функции. Тогда вы можете запустить,bubble_sort myarray
чтобы отсортировать myarray .Другое решение, которое использует внешние
sort
и справляется с любыми специальными символами (кроме NUL :)). Должен работать с bash-3.2 и GNU или BSDsort
(к сожалению, POSIX не включает-z
).Сначала посмотрите на перенаправление ввода в конце. Мы используем
printf
встроенные для записи элементов массива, заканчивающиеся нулем. Заключение в кавычки гарантирует, что элементы массива передаются как есть, а специфика оболочкиprintf
заставляет ее повторно использовать последнюю часть строки формата для каждого оставшегося параметра. То есть это эквивалентно чему-то вроде:Список элементов с нулевым символом в конце передается
sort
.-z
Опция заставляет его читать нулевые завершающие элементы, сортировать их и выход нуль-терминатор , а также. Если вам нужно получить только уникальные элементы, вы можете пройти,-u
так как он более переносим, чемuniq -z
.LC_ALL=C
Обеспечивает стабильный порядок сортировки независимо от локализации - иногда полезно для сценариев. Если вы хотитеsort
уважать язык, удалите его.<()
Конструкция получает дескриптор для чтения из породившего трубопровода, и<
перенаправляет стандартный вводwhile
петли к нему. Если вам нужен доступ к стандартному вводу внутри канала, вы можете использовать другой дескриптор - упражнение для читателя :).Теперь вернемся к началу.
read
Встроенный считывает выход из перенаправлены стандартного ввода. Установка пустогоIFS
отключает разделение слов, которое здесь не нужно - в результате,read
читается вся «строка» ввода в единственную предоставленную переменную.-r
опция отключает обработку escape, которая также нежелательна Наконец,-d ''
устанавливает разделитель строки в NUL - то есть говоритread
читать строки с нулем в конце .В результате цикл выполняется один раз для каждого последующего элемента массива с нулевым символом в конце, причем значение сохраняется в
e
. В примере просто помещаются элементы в другой массив, но вы можете предпочесть обрабатывать их напрямую :).Конечно, это только один из многих способов достижения одной и той же цели. На мой взгляд, это проще, чем реализовать полный алгоритм сортировки в bash, а в некоторых случаях это будет быстрее. Он обрабатывает все специальные символы, включая символы новой строки, и должен работать в большинстве распространенных систем. Самое главное, он может научить вас чему-то новому и удивительному в bash :).
источник
e
и устанавливать пустой IFS, используйте переменную REPLY.попробуй это:
Выход будет:
Задача решена.
источник
Если вы можете вычислить уникальное целое число для каждого элемента в массиве, например так:
затем вы можете использовать эти целые числа в качестве индексов массива, поскольку Bash всегда использует разреженный массив, поэтому не нужно беспокоиться о неиспользуемых индексах:
источник
мин сортировка:
источник
Эхо-содержимое new_array будет:
источник
Существует обходной путь для обычной проблемы пробелов и переносов:
Используйте символ , который не в исходном массиве (например ,
$'\1'
или$'\4'
или аналогичный).Эта функция выполняет работу:
Это отсортирует массив:
Это будет жаловаться, что исходный массив содержит символ временного решения:
описание
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 (без внешних процессов):
источник
Я не уверен, что вам понадобится внешняя программа сортировки в Bash.
Вот моя реализация простого алгоритма сортировки пузырьков.
Это должно напечатать:
источник
O(n^2)
. Кажется, я помню, что большинство алгоритмов сортировки используютO(n lg(n))
до конечной дюжины элементов или около того. Для конечных элементов используется сортировка выбора.источник
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
В духе bash / linux я бы выделил лучший инструмент командной строки для каждого шага.
sort
выполняет основную работу, но требует ввода, разделенного новой строкой, а не пробелом, поэтому очень простой конвейер выше просто делает:Содержимое массива эха -> заменить пробел новой строкой -> сортировать
$()
это повторить результат($())
это положить "отраженный результат" в массивеПримечание : как @sorontar упоминается в комментарии к другому вопросу:
источник
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[@]}"
. Другой антипаттерн:($())
помещает «отраженный результат» в массив . Конечно, нет! это ужасный антипаттерн, который ломается из-за расширения пути (разбивки) и разделения слов. Никогда не используйте этот ужас.