Я хочу порівняти два числа з плаваючою комою в сценарії оболонки. Наступний код не працює:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Я хочу порівняти два числа з плаваючою комою в сценарії оболонки. Наступний код не працює:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Відповіді:
Ви можете окремо перевірити цілі та дробові частини:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
Як сказано в коментарях fered, воно працює лише в тому випадку, якщо обидва числа мають дробові частини, і обидві дробові частини мають однакову кількість цифр. Ось версія, яка працює для цілих чи дробових чи будь-яких операторів bash:
#!/bin/bash
shopt -s extglob
fcomp() {
local oldIFS="$IFS" op=$2 x y digitx digity
IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
digitx=${x[1]:0:1} digity=${y[1]:0:1}
(( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
x[1]=${x[1]:1} y[1]=${y[1]:1}
done
[[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
[[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
(( ${x:-0} $op ${y:-0} ))
}
for op in '==' '!=' '>' '<' '<=' '>='; do
fcomp $1 $op $2 && echo "$1 $op $2"
done
1.00000000000000000000000001
це більше, ніж 2
.
Баш не розуміє арифметику з плаваючою комою. Він розглядає числа, що містять десяткову точку, як рядки.
Використовуйте замість awk або bc.
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
Якщо ви збираєтесь робити багато математичних операцій, можливо, краще покластися на python або perl.
Ви можете використовувати пакунки num-utils для простих маніпуляцій ...
Для більш серйозної математики див. Це посилання ... Тут описано кілька варіантів, наприклад.
Приклад numprocess
echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087
A programs for dealing with numbers from the command line
The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.
Includes these programs:
* numaverage: A program for calculating the average of numbers.
* numbound: Finds the boundary numbers (min and max) of input.
* numinterval: Shows the numeric intervals between each number in a sequence.
* numnormalize: Normalizes a set of numbers between 0 and 1 by default.
* numgrep: Like normal grep, but for sets of numbers.
* numprocess: Do mathematical operations on numbers.
* numsum: Add up all the numbers.
* numrandom: Generate a random number from a given expression.
* numrange: Generate a set of numbers in a range expression.
* numround: Round each number according to its value.
Ось bash
хак ... Він додає провідні 0 до цілого числа, щоб зробити порівняння рядка зліва направо значущим. Цей конкретний фрагмент коду вимагає, щоб і min, і val фактично мали десяткову точку і принаймні одну десяткову цифру.
min=12.45
val=10.35
MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min
вихід:
min=10.35
Для простих обчислень чисел з плаваючою комою (+ - * / та порівнянь) ви можете використовувати awk.
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
Або якщо у вас є ksh93 або zsh (не bash), ви можете використовувати вбудовану арифметику вашої оболонки, яка підтримує числа з плаваючою комою.
if ((min>val)); then ((val=min)); fi
Для більш вдосконалених обчислень з плаваючою комою шукайте bc . Він фактично працює на довільно-точні числа фіксованих точок.
Команда sort
має опцію -g
( --general-numeric-sort
), яку можна використовувати для порівняння на <
"менше ніж" або >
"більше, ніж", знаходячи мінімум чи максимум.
Ці приклади знаходять мінімум:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
Він працює з досить загальним позначенням чисел з плаваючою комою, як з E-Notation
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
Зауважте, що E-10
, зробивши перше число 0.000000001245
, дійсно менше, ніж 10.35
.
Стандарт з плаваючою комою, IEEE754 , визначає деякі особливі значення. Ці порівняння цікаві INF
для нескінченності. Існує також негативна нескінченність; Обидва є чітко визначеними значеннями в стандарті.
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
Щоб знайти максимальне використання sort -gr
замість того sort -g
, щоб змінити порядок сортування:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
Для реалізації порівняння <
("менше, ніж"), щоб його можна було використовувати в if
тощо, порівняйте мінімум з одним із значень. Якщо мінімум дорівнює значенню, порівняно з текстом , воно менше іншого значення:
$ a=12.45; b=10.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
0
a == min(a, b)
те саме, що a <= b
. Варто зазначити, що це не перевіряється строго менше, ніж все-таки. Якщо ви хочете це зробити, вам потрібно перевірити a == min(a, b) && a != max(a, b)
, іншими словамиa <= b and not a >= b
Просто використовуйте ksh
( ksh93
точно) або zsh
, які обидва підтримують арифметику з плаваючою комою:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
Редагувати: Вибачте, мені ksh93
вже було запропоновано. Зберігаючи свою відповідь лише для того, щоб зрозуміти, сценарій, розміщений у вступному питанні, можна використовувати без змін поза комутатором оболонки.
Edit2: Зауважте, що ksh93
вимагає, щоб зміст змісту відповідав вашій мові, тобто з французькою мовою, повинна використовуватись кома замість крапки:
...
min=12,45
val=10,35
...
Більш надійним рішенням є встановити локаль на початку сценарію, щоб переконатися, що він буде працювати незалежно від мови користувача:
...
export LC_ALL=C
min=12.45
val=10.35
...
.
(тобто не в половині світу, де знаходиться десятковий роздільник ,
). zsh
не має цього питання.
LC_ALL
, це також означає, що цифри не будуть відображатися (або вводитися) у бажаному форматі користувача. Дивіться unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/… щодо потенційно кращого підходу.
.
будь-якому випадку.
min=$(echo "${min}sa ${val}d la <a p" | dc)
Це використовує dc
калькулятор, щоб вирвати s
значення $min
в регістрі a
і d
збільшити значення $val
на верхній частині його основного стеку виконання. Потім він l
виводить вміст a
на верхню частину стека, в який момент він виглядає так:
${min} ${val} ${val}
<
Вискакує дві верхні записи з стека і порівнює їх. Отже, стек виглядає так:
${val}
Якщо верхній запис був меншим, ніж другий вгорі, він висуває вміст a
на верхню частину , тому стек виглядає так:
${min} ${val}
Інакше це нічого не робить, і стек все ще виглядає так:
${val}
Тоді просто p
промальовує верхній запис стека.
Отже, для вашої проблеми:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
Але:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
Чому б не використовувати старе, добре expr
?
Приклад синтаксису:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
Для справжніх виразів код виходу expr дорівнює 0, а рядок '1' надсилається до stdout. Зворотне для помилкового виразів.
Я перевірив це за допомогою GNU та FreeBSD 8 expr.
expr 1.09 '<' -1.1
буде друкувати 1
та виходити з 0
(успіх).
Щоб перевірити, чи є в порядку два (можливо дробові) номери, чи sort
є (розумно) переносним:
min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
echo min is smallest
else
echo val is smallest
fi
Однак якщо ви насправді хочете оновлювати мінімальне значення, то вам це не потрібно if
. Сортуйте числа та завжди використовуйте перше (найменше) одне:
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
Зазвичай я роблю подібні речі із вбудованим кодом python:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
0.5
та0.06
). Вам краще скористатися інструментом, який вже розуміє десяткові позначення.