Как сравнить число с плавающей запятой в сценарии оболочки

Я хочу сравнить два числа с плавающей запятой в сценарии оболочки. Следующий код не работает:

#!/bin/bash min=12.45 val=10.35 if (( $val < $min )) ; then min=$val fi echo $min 

11 Solutions collect form web for “Как сравнить число с плавающей запятой в сценарии оболочки”

Вы можете отдельно проверить целые и дробные части:

 #!/bin/bash min=12.45 val=12.35 if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then min=$val fi echo $min 

Как сказано в комментариях, он работает только в том случае, если оба числа имеют дробные части, а обе дробные части имеют одинаковое количество цифр. Вот версия, которая работает для целочисленного или дробного и любого оператора bash:

 #!/bin/bash shopt -s extglob fcomp() { local oldIFS="$IFS" op=$2 xy 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 

Баш не понимает арифметику с плавающей запятой. Он обрабатывает числа, содержащие десятичную точку в виде строк.

Вместо этого используйте 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 для простых манипуляций …

Более серьезную математику см. В этой ссылке … В ней описываются несколько вариантов, например.

  • R / Rscript (статистическая вычислительная и графическая система GNU R)
  • октава (в основном совместимая с Matlab)
  • bc (язык калькулятора произвольной точности GNU bc)

Пример 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 . Он фактически работает с произвольными точками фиксированной точки.

Чтобы работать с таблицами чисел, найдите R ( пример ).

Использовать числовую сортировку

У команды sort есть опция -g ( --general-numeric-sort ), которая может использоваться для сравнений на < , "меньше" или > , "больше чем", путем нахождения минимума или максимума.

Эти примеры находят минимум:

 $ printf '12.45\n10.35\n' | sort -g | head -1 10.35 

Поддержка E-Notation

Он работает с довольно общими обозначениями чисел с плавающей запятой, например, с помощью 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 , sort -gr порядок сортировки:

 $ 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 

Просто используйте 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 ... 
 min=$(echo "${min}sa ${val}d la <ap" | dc) 

Это использует калькулятор dc для того, чтобы s разорвать значение для $min в регистре a а d увеличивает значение $val в верхней части своего основного стека выполнения. Затем он отображает содержимое a на верхнюю часть стека, после чего он выглядит так:

 ${min} ${val} ${val} 

«Сбрасывает верхние две записи из стека и сравнивает их. Таким образом, стек выглядит следующим образом:

 ${val} 

Если верхняя запись была меньше второй, она подталкивает содержимое a сверху, поэтому стек выглядит так:

 ${min} ${val} 

Иначе это ничего не делает, и стек все равно выглядит так:

 ${val} 

Затем он просто устанавливает верхнюю запись стека.

Итак, для вашей проблемы:

 min=12.45 val=12.35 echo "${min}sa ${val}d la <ap" | dc ###OUTPUT 12.35 

Но:

 min=12.45 val=12.55 echo "${min}sa ${val}d la <ap" | 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.

Чтобы проверить, имеются ли два (возможно, дробных) номера, 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 
  • UNIX KSH / BASH Вызов по ссылке с использованием массива (в GNU bash 4.2)
  • Копировать команду с аргументами командной строки
  • Что вы используете, когда не можете использовать Bash?
  • Почему я получаю разные результаты при запуске моего shellscript вручную, когда я запускаю его с помощью cron?
  • Нужен скрипт, который генерирует файлы из слов в одном текстовом файле
  • Значение различных параметров, используемых при запуске сценария оболочки
  • Копирование новейших файлов
  • Сохранение и восстановление вкладок терминала с заголовком пользовательских вкладок
  • Как отобразить результат df каждые 30 секунд?
  • Хранение более длинного текстового вывода в памяти в переменных оболочки vs Запись на диск
  • Как я могу использовать регулярное выражение для извлечения текста, следующего за двумя условиями, в переменную?
  • Linux и Unix - лучшая ОС в мире.