2016-03-21 3 views
1

У меня есть каталог, в основном номера версий, и я пытаюсь определить тот, который отсортирован по версиям непосредственно перед другим конкретным.выбрать из отсортированного списка в bash

Основой здесь, опасаясь, что это проблема XY, является то, что эти номера версий получены из тегов git для веб-контента, и мы символизируем каталог public_html для виртуального хоста сайта в каталог с версией.

Это выглядит примерно так:

drwxr-xr-x 8 graham www 17 Oct 17 2013 v2.0.10 
drwxr-xr-x 8 graham www 17 Oct 17 2013 v2.0.11 
drwxrwxr-x 8 graham www 17 Aug 29 2013 v2.0.8 
drwxr-xr-x 8 brian www 17 Oct 17 2013 v2.0.9 
... 
drwxr-xr-x 9 graham www 21 Aug 5 2015 v4.19.0 
drwxr-xr-x 10 graham www 19 Dec 17 2014 v4.2.0 
drwxr-xr-x 9 brian www 21 Aug 10 2015 v4.20.0 
drwxr-xr-x 9 brian www 21 Aug 10 2015 v4.20.0-fail 
drwxr-xr-x 9 graham www 21 Aug 11 2015 v4.20.1 
... 
drwxr-xr-x 9 graham www 19 Mar 14 11:00 v4.35.0 
drwxr-xr-x 9 graham www 19 Mar 14 13:28 v4.36.0 
drwxr-xr-x 9 graham www 19 Mar 16 10:58 v4.36.1 
lrwxr-xr-x 1 graham www 11 Mar 16 11:13 public_html -> v4.36.1 

Символическая ссылка не всегда может указывать на самой последней версии (например, если мы должны были отступить из-за изменения). Моя цель - найти правильно названный каталог (т. Е. Мы пропустим *-fail и т. П.), Который будет отсортирован до того, на который указывает public_html.

Я в FreeBSD, так что моя stat команда работает хорошо, как это:

read target <<<"$(stat -f'%Y' "${base}/public_html")" 

Если вы в Linux, я понимаю, вы могли бы получить тот же эффект, что-то вроде этого:

target="$(stat -c '%N' "${base}/public_html")" 
target="${target#* -> ?}"; target="${target%?}" 

Но отсюда я не уверен, как я собираю номер версии. Обратите внимание, что я во FreeBSD, поэтому моя команда sort не имеет опции -V. Я пробовал два метода.

Первый обрабатывать петлю, а на содержимое каталога:

shopt -s extglob 
while read this; do 
    [[ "$this" = "$target" ]] && break 
    previous="$this" 
done < $(ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -n -t . -k1,1 -k2,2 -k3,3) 

Второй очень похож, разборе массив вместо:

shopt -s extglob 
a=($(ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -n -t . -k1,1 -k2,2 -k3,3)) 
for ((n=0; n<${#a[@]}; n++)); do 
    [[ "${a[$n]}" = "$target" ]] && break 
    previous="${a[$n]}" 
done 

Где оба эти падают является sort, в котором первое поле не сортируется. Я подозреваю, что это потому, что оно не числовое (с «v» в начале, с разделителем «.»). Есть ли способ указать несколько разделителей или иначе «игнорировать» v как-нибудь?

Или есть лучший способ справиться с этим, чем использовать внешнюю команду sort, которая имеет непереносимые опции? Возможно, что-то внутреннее для баша?

Спасибо.

ответ

0

Возможно, не полного решения, но вы можете быть в состоянии уйти со следующим:

ls -1d v+([0-9]).+([0-9]).+([0-9]) | sort -t . -k1,1 -k2,2n -k3,3n 

Разница здесь в том, что -n для числового полей применяется только ко второму и третьему полю , поэтому вы продолжите сортировку по алфавиту в первом поле.

Это не поможет вам в прыжке от v9.x.x к v10.x.x, но, как вы ушли из v2 в v4 в чуть менее трех лет, может быть, это «достаточно хорошо».

ТАКЖЕ:

  • Вы можете запустить это в два этапа. Первый этап определяет самый последний номер главной версии с чем-то вроде major=$(ls -1d v[0-9]* | tail -1); major=${major%%.*}", а затем последующий сортировка основана на a=$(ls ${major}.+([0-9]).+([0-9])). Затем вы можете безопасно сортировать только числовые поля.
  • a bash implementation of qsort, который вы можете адаптировать, чтобы сделать что-то, что является полностью внутренним для bash. Это потребует некоторого взлома, чтобы заставить его работать с вашим массивом.
0

Вы можете отделить альфа-префикс, сортировать численно подразделам полями, и слейте назад, что я добавил v10

$ ls -1 v* | sed 's/^v/v./' | sort -t. -k2,2n -k3,3n -k4n | sed 's/^v\./v/' 

v2.0.8 
v2.0.9 
v2.0.10 
v2.0.11 
v4.2.0 
v4.19.0 
v4.20.0 
v4.20.0-fail 
v4.20.1 
v4.26.1 
v4.35.0 
v4.36.0 
v10.0.1 

примечание. *, Чтобы убедиться, что это на будущее.

+0

где вы видели '-V' вариант используется? Это обходной путь из-за отсутствия варианта версии. – karakfa

+0

Извините, моя ошибка. Не могу прочитать сегодня днем. –

0

Это чистый Bash раствор (для readlink кроме) делает линейный поиск вместо сортировки:

VERSION_RX='^v([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)$' 

target=$(readlink "$base/public_html") 

# target == v1.22.333 -> target_key == 000000001_000000022_000000333 
[[ $target =~ $VERSION_RX ]] || exit 1 
printf -v target_key '%09d_%09d_%09d' \ 
    "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" 

pred_dir= 
pred_key= 

for path in "$base"/v*.*.* ; do 
    dir=${path##*/} 
    [[ $dir == "$target" ]] && continue 
    [[ $dir =~ $VERSION_RX ]] || continue 
    printf -v key '%09d_%09d_%09d' \ 
     "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" 

    [[ $key > $target_key ]] && continue 

    if [[ -z $pred_key || $key > $pred_key ]] ; then 
     pred_dir=$dir 
     pred_key=$key 
    fi 
done 

echo "$pred_dir" 
Смежные вопросы