Предположим, что у меня есть список путей файлов, хранящихся в массиве
filearray=("dir1/0010.pdf" "dir2/0003.pdf" "dir3/0040.pdf" )
Я хочу отсортировать элементы в массиве в соответствии с базовыми именами имен файлов в числовом порядке
sortedfilearray=("dir2/0003.pdf" "dir1/0010.pdf" "dir3/0040.pdf")
Как я могу это сделать?
Я могу сортировать только их базовые части:
basenames=()
for file in "${filearray[@]}"
do
filename=${file##*/}
basenames+=(${filename%.*})
done
sortedbasenamearr=($(printf '%s\n' "${basenames[@]}" | sort -n))
Я думаю о
- создание ассоциативного массива, ключи которого являются базовыми именами, а значения - именами путей, поэтому доступ к путевым именам всегда осуществляется через базовые имена.
- создание другого массива только для базовых имен и применение
sort
к массиву базовых имен.
Спасибо.
dir1
dir2
только что составлены, и они на самом деле являются произвольными путями.Ответы:
В отличие от ksh или zsh, bash не имеет встроенной поддержки сортировки массивов или списков произвольных строк. Он может сортировать глобусы или выходные данные
alias
илиset
илиtypeset
(хотя последние 3 не в порядке сортировки локали пользователя), но это практически невозможно использовать здесь.В инструментарии POSIX нет ничего, что могло бы легко сортировать произвольные списки строк¹ (
sort
сортирует строки, поэтому только короткие (LINE_MAX часто короче PATH_MAX) последовательности символов, отличных от NUL и новой строки, в то время как пути к файлам являются непустыми последовательностями байтов, отличными от других. чем 0).Таким образом, хотя вы можете реализовать свой собственный алгоритм сортировки в
awk
(используя<
оператор сравнения строк) или дажеbash
(используя[[ < ]]
) для произвольных путейbash
, переносимо, проще всего прибегнуть кperl
:С помощью
bash4.4+
вы можете сделать:Это дает
strcmp()
похожий порядок. Для порядка, основанного на правилах сортировки локали, таких как globs или выходные данныеls
, добавьте-Mlocale
аргумент вperl
. Для числовой сортировки (больше похожей на GNU, такsort -g
как она поддерживает числа, подобные+3
,1.2e-5
а не разделители тысяч, хотя и не шестнадцатеричные), используйте<=>
вместоcmp
(и снова-Mlocale
для того, чтобы десятичная отметка пользователя учитывалась, как дляsort
команды).Вы будете ограничены максимальным размером аргументов команды. Чтобы избежать этого, вы можете передать список файлов
perl
на его стандартный ввод вместо аргументов:В более старых версиях
bash
вы могли бы использоватьwhile IFS= read -rd ''
цикл вместоreadarray -d ''
или получитьperl
для вывода список путей, правильно заключенных в кавычки, чтобы вы могли передать егоeval "array=($(perl...))"
.С помощью
zsh
вы можете подделать расширение glob, для которого вы можете определить порядок сортировки:С помощью
reply=($filearray)
мы фактически форсируем глобальное расширение (которое изначально было просто/
) быть элементами массива. Затем мы определяем порядок сортировки на основе хвоста имени файла.Для
strcmp()
порядка, подобного языку, установите языковой стандарт на C. Для числовой сортировки (аналогично GNUsort -V
,sort -n
которая не имеет существенного значения при сравнении1.4
и1.23
(например, в языковых стандартах, где.
используется десятичная метка), добавьтеn
квалификатор glob.Вместо этого
oe{expression}
вы также можете использовать функцию для определения порядка сортировки, например:или более продвинутые, такие как:
(так
a/foo2bar3.pdf
(2,3 числа) сортирует послеb/bar1foo3.pdf
(1,3), но доc/baz2zzz10.pdf
(2,10)) и использует как:Конечно, они могут быть применены к реальным шарам, поскольку это то, для чего они в первую очередь предназначены. Например, для списка
pdf
файлов в любом каталоге, отсортированного по базовому имени / хвосту:¹ Если
strcmp()
приемлема сортировка на основе и для коротких строк, вы можете преобразовать строки в их шестнадцатеричное кодирование с помощьюawk
до передачи вsort
и преобразовать обратно после сортировки.источник
sort
в GNU coreutils позволяет настраивать разделитель полей и ключ. Вы устанавливаете в/
качестве разделителя полей и сортируете на основе второго поля для сортировки по базовому имени, а не по всему пути.printf "%s\n" "${filearray[@]}" | sort -t/ -k2
будет производитьисточник
sort
, а не расширение GNU. Это будет работать, если все пути имеют одинаковую длину.some/long/path/0011.pdf
? Насколько я вижу из его справочной страницы, онаsort
не содержит опций сортировки по последнему полю.Сортировка с поглазеть выражением (поддерживается Баш «с
readarray
):Пример массива имен файлов, содержащих пробелы :
Выход:
Доступ к одному элементу:
Это предполагает, что ни один путь к файлу не содержит символов новой строки. Обратите внимание, что числовая сортировка значений в
@val_num_asc
относится только к ведущей числовой части ключа (в данном примере - ни одной) с отступом к лексическому сравнению (на основеstrcmp()
порядка сортировки локали) для связей.источник
Сортировка имен файлов с символами новой строки в их именах вызовет проблемы на
sort
шаге.Он генерирует
/
список с неограниченным доступом,awk
который содержит базовое имя в первом столбце и полный путь в качестве оставшихся столбцов:Это то, что отсортировано, и
cut
используется для удаления первого/
столбца с неограниченным количеством. Результат превращается в новыйbash
массив.источник
/some/dir/
.a/x.c++ b/x.c-- c/x.c++
будет отсортирован в таком порядке, хотя-
сортируется раньше,+
потому что-
,+
и/
первичный вес равен IGNORE (поэтому сравнениеx.c++/a/x.c++
сx.c--/b/x.c++
первым сравниваетсяxcaxc
сxcbxc
, и только в случае связей будут другие веса (где-
раньше+
) будет рассмотрено./x/
вместо/
, но это не учитывало бы случай, когда в локали C на системах, основанных на ASCII,a/foo
сортировался бы,a/foo.txt
например, потому что/
сортирует после.
.Поскольку «
dir1
иdir2
являются произвольными путями», мы не можем рассчитывать на то, что они состоят из одного каталога (или одного и того же количества каталогов). Поэтому нам нужно преобразовать последнюю косую черту в путевых именах во что-то, чего нет в других местах путевого имени. Предположим, что символ@
не встречается в ваших данных, вы можете отсортировать по базовому имени следующим образом:Первая
sed
команда заменяет последнюю косую черту в каждом пути на выбранный разделитель, вторая отменяет изменение. (Для простоты я предполагаю, что имена путей могут быть доставлены по одному на строку. Если они находятся в переменной оболочки, сначала преобразуйте их в формат по одной строке.)источник
cat pathnames | sed 's|\(.*\)/|\1'$'\4''|' | sort -t$'\4' -k+2nr | sed 's|'$'\4''|/|'
. (Я только что взял\4
из таблицы ASCII. Видимо "КОНЕЦ ТЕКСТА"?)\4
есть^D
(control-D). Если вы не введете его самостоятельно в терминале, это обычный управляющий символ. Другими словами, безопасно использовать таким способом.Краткое (и несколько быстрое) решение: добавив индекс массива к именам файлов и отсортировав их, мы позже можем создать отсортированную версию на основе отсортированных признаков.
Это решение требует только встроенных команд bash и
sort
двоичного файла, а также работает со всеми именами файлов, которые не включают\n
символ новой строки .Для каждого файла мы отображаем его базовое имя с начальным индексом, добавленным так:
а затем отправили через
sort -n
.После этого мы перебираем выходные строки, извлекаем старый индекс с расширением переменной bash
${line##* }
и вставляем этот элемент в конец нового массива.источник
Это сортирует путем добавления к путевым именам файлов базового имени, числовой сортировки и последующего удаления базового имени в начале строки:
Было бы более эффективно, если бы у вас были имена файлов в списке, которые можно было бы передать непосредственно через канал, а не как массив оболочки, потому что фактическая работа выполняется
sed | sort | sed
структурой, но этого достаточно.Впервые я столкнулся с этой техникой при кодировании на Perl; на этом языке это было известно как преобразование Шварца .
В Bash преобразование, как указано здесь в моем коде, завершится ошибкой, если у вас есть нечисловые значения в базовом имени файла. В Perl это можно кодировать гораздо безопаснее.
источник
$@
или$*
из аргументов командной строки для запуска сценарияДля одинаковой глубины имен файлов.
объяснение
Информация взята у человека рода.
Полученный массив печати
источник