POSIXly вы можете сделать:
sort -t '(' -k2n < file
Это устанавливает разделитель полей на (
и сортирует по второму полю (или, скорее, по части строки, начинающейся со второго поля )в числовом виде.
В качестве альтернативы вы можете оставить разделитель полей по умолчанию (для перехода от не-пустого поля к пустому ), где 5-е -е поле будет похоже на " (12"
и использовать:
sort -k5.3n < file
(, т. е. численно отсортировать часть строки, начинающуюся с 3-го -го символа 5-го -го поля ).
Для ничьих в игру вступает последний -порядок сортировки, и это лексическое сравнение полной строки (, которое удобно здесь даст вам хронологический порядок ).
Если вы хотите отсортировать связи по имени диска, вы можете использовать:
sort -t '(' -k2n -k1.21
(второй ключ — часть строки, начинающаяся с 21 st символа, лексическое сравнение)
Переносимо, массивы не требуются (только позиционные параметры )и работает с пробелами и новыми строками:
flag=''; for a in "$@"; do set -- "$a" ${flag-"$@"}; unset flag; done
Пример:
$ set -- one "two 22" "three
> 333" four
$ printf '<%s>' "$@"; echo
<one><two 22><three
333><four>
$ flag=''; for a in "$@"; do set -- "$a" ${flag-"$@"}; unset flag; done
$ printf '<%s>' "$@"; echo
<four><three
333><two 22><one>
Значение flag
управляет расширением ${flag-"$@"}
. Когда установлено flag
, оно расширяется до значения flag
(, даже если оно пусто ). Таким образом, когда flag
равно flag=''
, ${flag....}
расширяется до пустого значения и удаляется оболочкой, поскольку оно не заключено в кавычки. Когда flag
сбрасывается, значение ${flag-"$@"}
расширяется до значения справа от -
, это расширение "$@"
, поэтому оно становится всеми позиционными аргументами (в кавычках, нет пустое значение будет стерто ). Кроме того, переменная flag
становится стертой (неустановленной ), что не влияет на следующий код.
Если мы не хотим использовать массив для временного хранения, мы можем использовать тот факт, что for
цикл всегда перебирает неизменный статический набор элементов. В некотором смысле мы можем использовать сам цикл как временное хранилище позиционных параметров при перестроении списка в обратном порядке.
Чтобы сделать это, нам также нужно очистить список на первой итерации. В приведенном ниже коде используется простой флаг, чтобы определить, нужно ли это делать или нет. Когда список пуст, флаг переключается.
flag=true
for value do
if "$flag"; then
set --
flag=false
fi
set -- "$value" "$@"
done
К сожалению, это довольно медленно, так как список позиционных параметров эффективно перестраивается на каждой итерации(set -- some-list
устанавливает все позиционные параметры ). Оболочке bash
требуется около 50 секунд, чтобы преобразовать целые числа от 1 до 10000, а zsh
— чуть более 15 секунд.
Использование уловки Исаака с ${flag-"$@"}
(, которая расширяется до "$@"
только в том случае, если flag
не установлено ), на самом деле все работает медленнее; 1 минута 50 секунд (! )в bash
и 25 секунд в zsh
.
Я предполагаю, что это связано с некоторыми особенностями реализации в том, как оболочки выполняют тест на $flag
и/или расширяют "$@"
для расширения ${flag-"$@"}
(оболочка, возможно, расширяет "$@"
дважды внутри? ).
Если мы позволим себе использовать массив в качестве временного хранилища, (это не будет стандартным ,но все же довольно переносимый поскольку мы часто знаем, для какой оболочки мы пишем наши скрипты ), мы можем использовать значение$#
(количество позиционных параметров )в качестве индекса для хранения текущее значение при переборе позиционных параметров. Уменьшение этого значения с помощью shift
на каждой итерации дает эффект вставки значений от конца массива к началу.
В bash
массивы начинаются с индекса 0, а поскольку shift
идет после присваивания, последний позиционный параметр будет сохранен с индексом 1, а не 0. Это не влияет на то, как работает код в bash
, он по-прежнему будет генерировать правильный результат, но он также работает в zsh
(, который по умолчанию использует 1 -индексов массива на основе ).
Код:
tmp=()
for value do
tmp[$#]=$value
shift
done
set -- "${tmp[@]}"
При использовании bash
или zsh
для обращения целых чисел от 1 до 10000 требуется около 0,6 секунды.
Скопировано из этого моего ответа в Bash -вывести перевернутый список файлов с помощью glob , чтобы перевернуть список позиционных параметров POSIXly:
eval "set -- $(awk 'BEGIN {for (i = ARGV[1]; i; i--) printf " \"${"i"}\""}' "$#")"
Или чуть более разборчиво на несколько строк:
eval "set -- $(
awk '
BEGIN {
for (i = ARGV[1]; i; i--)
printf " \"${" i "}\""
}' "$#"
)"
Идея заключалась в том, чтобы использовать awk
для создания шелл-кода set -- "${3}" "${2}" "${1}"
для eval
для интерпретации, например, когда "$@"
имеет 3 элемента.
Для больших списков это, вероятно, будет значительно быстрее, чем использование цикла оболочки, особенно такого, который перестраивает список на каждой итерации. Код awk
можно заменить циклом оболочки, который дает тот же результат (, что и @mosvy, показанный в комментариях ), но в моих тестах с bash5+gawk4.1 он по-прежнему в два раза медленнее, за исключением очень короткие списки.
В zsh
вы должны использовать флаг параметра Oa
, который явно предназначен для реверсирования массива:
set -- "${(Oa)@}"
В моей системе (немного медленнее, чем у @Kusalananda ), и в списке позиционных параметров, полученном с set $(seq 10000)
, с bash5 + gawk4.2.1,этот подход eval
занимает 0,4 с, в то время как @Kusalananda занимает 1 минуту, а @Isaac занимает 2 минуты(zsh
подход Oa
занимает около 2 миллисекунд ).
Для sh
и awk
из busybox 1.30.1 эти тайминги становятся :0,06 с, 11 с, 11 с соответственно.