- Функция
V
- решение для чистого bash, никаких внешних утилит не требуется. - Поддержка
=
==
!=
<
<=
>
и >=
(лексикографический). - Дополнительное сравнение хвостовых букв:
1.5a < 1.5b
- Сравнение неравной длины:
1.6 > 1.5b
- Считывает слева направо:
if V 1.5 '<' 1.6; then ...
.
<>
# Sample output
# Note: ++ (true) and __ (false) mean that V works correctly.
++ 3.6 '>' 3.5b
__ 2.5.7 '<=' 2.5.6
++ 2.4.10 '<' 2.5.9
__ 3.0002 '>' 3.0003.3
++ 4.0-RC2 '>' 4.0-RC1
<>
function V() # $1-a $2-op $3-$b
# Compare a and b as version strings. Rules:
# R1: a and b : dot-separated sequence of items. Items are numeric. The last item can optionally end with letters, i.e., 2.5 or 2.5a.
# R2: Zeros are automatically inserted to compare the same number of items, i.e., 1.0 < 1.0.1 means 1.0.0 < 1.0.1 => yes.
# R3: op can be '=' '==' '!=' '<' '<=' '>' '>=' (lexicographic).
# R4: Unrestricted number of digits of any item, i.e., 3.0003 > 3.0000004.
# R5: Unrestricted number of items.
{
local a=$1 op=$2 b=$3 al=${1##*.} bl=${3##*.}
while [[ $al =~ ^[[:digit:]] ]]; do al=${al:1}; done
while [[ $bl =~ ^[[:digit:]] ]]; do bl=${bl:1}; done
local ai=${a%$al} bi=${b%$bl}
local ap=${ai//[[:digit:]]} bp=${bi//[[:digit:]]}
ap=${ap//./.0} bp=${bp//./.0}
local w=1 fmt=$a.$b x IFS=.
for x in $fmt; do [ ${#x} -gt $w ] && w=${#x}; done
fmt=${*//[^.]}; fmt=${fmt//./%${w}s}
printf -v a $fmt $ai$bp; printf -v a "%s-%${w}s" $a $al
printf -v b $fmt $bi$ap; printf -v b "%s-%${w}s" $b $bl
case $op in
'<='|'>=' ) [ "$a" ${op:0:1} "$b" ] || [ "$a" = "$b" ] ;;
* ) [ "$a" $op "$b" ] ;;
esac
}
Код объяснения
Строка 1 : определить локальные переменные:
a
, op
, b
- операнды сравнения и оператор, т. Е. «3,6»> «3,5a». al
, bl
-хвосты букв a
и b
, инициализированные для элемента хвоста, т. е. "6" и "5a".
строки 2, 3 : обрезка слевацифры от хвостовых элементов, поэтому остаются только буквы, если таковые имеются, т. е. "" и "a".
Строка 4 : буквы справа от a
и b
дооставьте только последовательность числовых элементов в качестве локальных переменных ai
и bi
, т. е. «3,6» и «3,5».Известный пример: "4.01-RC2"> "4.01-RC1" дает ai = "4.01" al = "- RC2" и bi = "4.01" bl = "- RC1".
Строка 6: определить локальные переменные:
ap
, bp
- нулевые правые отступы для ai
и bi
.Начните с того, что оставьте только точки между элементами, число которых равно числу элементов a
и b
соответственно.
Строка 7 : Затем добавьте "0"после каждой точки создавать маски.
Строка 9 : локальные переменные:
w
- ширина элемента fmt
- строка формата printf, рассчитывается x
- временно - При
IFS=.
bash разделяет значения переменных на '.'.
Линия 10 : вычисление w
, максимальной ширины элемента, которая будет использоваться для выравнивания элементов для лексикографического сравнения.В нашем примере w = 2.
Строка 11 : создайте формат выравнивания printf, заменив каждый символ $a.$b
на %${w}s
, то есть «3.6»> «3.5a»дает "% 2s% 2s% 2s% 2s".
Строка 12 : «printf -va» устанавливает значение переменной a
.Это эквивалентно a=sprintf(...)
во многих языках программирования.Обратите внимание, что здесь, благодаря эффекту IFS =.аргументы printf
разбиваются на отдельные элементы.
С первыми printf
элементами a
добавляются пробелы слева, в то время как достаточное количество элементов "0" добавляется из bp
, чтобы обеспечитьрезультирующая строка a
может быть по смыслу сравнена с аналогично отформатированным b
.
Обратите внимание, что мы добавляем bp
- не ap
к ai
, поскольку ap
и bp
могут иметь разные значенияЭто приводит к тому, что a
и b
имеют одинаковую длину.
Со вторым printf
мы добавляем буквенную часть al
к a
с достаточным дополнением, чтобы сделать возможным сравнение.Теперь a
готов для сравнения с b
.
Строка 13 : То же, что и строка 12, но для b
.
Строка 15 : Разделить случаи сравнения между не встроенными (<=
и >=
) и встроенными операторами.
Строка 16 : Если оператор сравнения <=
затем проверьте на a<b or a=b
- соответственно >=
a<b or a=b
Строка 17 : проверка встроенных операторов сравнения.
<>
# All tests
function P { printf "$@"; }
function EXPECT { printf "$@"; }
function CODE { awk $BASH_LINENO'==NR{print " "$2,$3,$4}' "$0"; }
P 'Note: ++ (true) and __ (false) mean that V works correctly.\n'
V 2.5 '!=' 2.5 && P + || P _; EXPECT _; CODE
V 2.5 '=' 2.5 && P + || P _; EXPECT +; CODE
V 2.5 '==' 2.5 && P + || P _; EXPECT +; CODE
V 2.5a '==' 2.5b && P + || P _; EXPECT _; CODE
V 2.5a '<' 2.5b && P + || P _; EXPECT +; CODE
V 2.5a '>' 2.5b && P + || P _; EXPECT _; CODE
V 2.5b '>' 2.5a && P + || P _; EXPECT +; CODE
V 2.5b '<' 2.5a && P + || P _; EXPECT _; CODE
V 3.5 '<' 3.5b && P + || P _; EXPECT +; CODE
V 3.5 '>' 3.5b && P + || P _; EXPECT _; CODE
V 3.5b '>' 3.5 && P + || P _; EXPECT +; CODE
V 3.5b '<' 3.5 && P + || P _; EXPECT _; CODE
V 3.6 '<' 3.5b && P + || P _; EXPECT _; CODE
V 3.6 '>' 3.5b && P + || P _; EXPECT +; CODE
V 3.5b '<' 3.6 && P + || P _; EXPECT +; CODE
V 3.5b '>' 3.6 && P + || P _; EXPECT _; CODE
V 2.5.7 '<=' 2.5.6 && P + || P _; EXPECT _; CODE
V 2.4.10 '<' 2.4.9 && P + || P _; EXPECT _; CODE
V 2.4.10 '<' 2.5.9 && P + || P _; EXPECT +; CODE
V 3.4.10 '<' 2.5.9 && P + || P _; EXPECT _; CODE
V 2.4.8 '>' 2.4.10 && P + || P _; EXPECT _; CODE
V 2.5.6 '<=' 2.5.6 && P + || P _; EXPECT +; CODE
V 2.5.6 '>=' 2.5.6 && P + || P _; EXPECT +; CODE
V 3.0 '<' 3.0.3 && P + || P _; EXPECT +; CODE
V 3.0002 '<' 3.0003.3 && P + || P _; EXPECT +; CODE
V 3.0002 '>' 3.0003.3 && P + || P _; EXPECT _; CODE
V 3.0003.3 '<' 3.0002 && P + || P _; EXPECT _; CODE
V 3.0003.3 '>' 3.0002 && P + || P _; EXPECT +; CODE
V 4.0-RC2 '>' 4.0-RC1 && P + || P _; EXPECT +; CODE
V 4.0-RC2 '<' 4.0-RC1 && P + || P _; EXPECT _; CODE