преобразовать научную нотацию в десятичную в bash

Я хотел бы преобразовать число, хранящееся в экспоненциальном представлении, в десятичное число с плавающей запятой, чтобы затем выполнить некоторые сравнения данных. Это делается в bash-скрипте — вот небольшой фрагмент кода:

while read track_id landfall_num gate_id pres_inter
do
  if [[ $landfall_num == 0001 ]]
  then
     start_flag = true
     echo DING DING $start_flag
     if [[ $pres_inter < 97000 ]]
     then
        echo Strong Storm From North $track_id, $gate_id, $pres_inter
     fi
  fi
done < $file

Моя проблема в том, что мой операнд < выбирает в основном все значения давления, которые хранятся в экспоненциальном представлении, когда я использую <, и ничего, когда я использую >. Я смотрю на измерения атмосферного давления в паскалях, а не в миллибарах.

Вот пример вывода:

Strong Storm From North 0039988 0017 1.0074E+05

Strong Storm From North 0037481 0018 9.9831E+04

Ни один из этих штормов не должен соответствовать критериям отбора!


person kimmyjo221    schedule 11.12.2012    source источник
comment
Почему бы просто не использовать однострочник Perl?   -  person Brian Vandenberg    schedule 11.12.2012


Ответы (3)


Во-первых, bash не может выполнять арифметические операции с плавающей запятой. Во-вторых, bash не знает научной нотации (даже для целых чисел).

Первое, что вы можете попробовать, если вы абсолютно уверены, что все ваши числа являются целыми числами: преобразуйте их в десятичную запись: printf с радостью сделает это за вас:

printf -v pres_inter "%.f" "$pres_inter"

(%.f округляется до ближайшего целого числа).

затем используйте арифметику bash:

if (( pres_inter < 97000 )); then ....

Это решение прекрасно и не использует никаких внешних команд или подоболочек: скорость и эффективность!

Теперь, если вы имеете дело с нецелыми числами, вам лучше использовать bc для выполнения арифметических действий: но, поскольку bc запаздывает и плохо работает с научной записью, вы все равно должны преобразовать свои числа в десятичную запись. Итак, что-то вроде следующего должно делать:

printf -v pres_inter "%f" "$pres_inter"
if (( $(bc -l <<< "$pres_inter<97000") )); then ...

Конечно, это медленнее, так как вы разветвляете bc. Если вас устраивают целые числа, просто придерживайтесь первой возможности, которую я дал.

Сделанный!

person gniourf_gniourf    schedule 11.12.2012

Я думаю, что лучший способ сделать это - использовать awk. Например, в случае числа "1.0074E+05"

echo "1.0074E+05" | awk -F"E" 'BEGIN{OFMT="%10.10f"} {print $1 * (10 ^ $2)}'

выход:

100740.0000000000

очевидно, вы можете использовать меньшую точность, чем 10 десятичных знаков :)

person Gabriele Amorosino    schedule 22.05.2019

Для числового сравнения вам необходимо использовать:

  • -eq вместо ==
  • -ne вместо !=
  • -lt вместо <
  • -le вместо <=
  • -gt вместо >
  • -ge вместо >=

И исправить некоторые проблемы с синтаксисом:

while read track_id landfall_num gate_id pres_inter
do
  landfall_num=$(printf "%f", "$landfall_num")
  if [[ "$landfall_num" -eq 1 ]]
  then
     start_flag="true"
     echo "DING DING $start_flag"
     if [[ "$pres_inter" -lt 97000 ]]
     then
        echo "Strong Storm From North $track_id, $gate_id, $pres_inter"
     fi
  fi
done < "$file"

Обновлено:

Вот как вы конвертируете число в экспоненциальном представлении в целое число:

printf "%.0f\n", 1.0062E+05

например, печатает:

100620

Так что:

landfall_num=$(printf "%f", "$landfall_num")

преобразует landfall_num из научной записи в обычное десятичное целое число.

person sampson-chen    schedule 11.12.2012
comment
благодарю вас. Просто любопытно - я только что прошел курс программирования оболочки на прошлой неделе, и они не говорили обо всех кавычках. Почему вы считаете этот синтаксис лучшим? Спасибо! - person kimmyjo221; 11.12.2012
comment
Кроме того, я получаю следующую ошибку: строка 28: [[: 1.0062E+05: синтаксическая ошибка: неверный арифметический оператор (токен ошибки — .0062E+05) - person kimmyjo221; 11.12.2012
comment
Почти всегда лучше заключать переменные в двойные кавычки, чтобы исключить такие угловые случаи, как пустые строки или строки, содержащие символы новой строки. - person sampson-chen; 11.12.2012
comment
Не используйте [[ для арифметических сравнений, кроме как в сочетании с каким-либо другим типом проверки, где чище использовать одну команду. Обычно для арифметики используйте (()). Bash не будет обрабатывать числа с плавающей запятой, поэтому ни один из подходов в любом случае не будет работать. - person ormaaj; 11.12.2012
comment
@ kimmyjo221 это потому, что научная нотация еще не была преобразована. Сам bash не поддерживает сравнения с плавающей запятой, если целые числа в порядке, см. обновленный ответ. - person sampson-chen; 11.12.2012
comment
целые числа были бы в порядке! Не могли бы вы описать команду awk, которую я мог бы использовать. Как я уже сказал - очень новичок в сценариях оболочки! Спасибо :) - person kimmyjo221; 11.12.2012
comment
Это не проблема использования -lt против <, это проблема научных обозначений... Ваша строка if [[ "$pres_inter" -lt 97000 ]] потерпит неудачу. - person gniourf_gniourf; 11.12.2012
comment
И использование подоболочки для получения значения printf задерживается. Используйте его вариант -v... в любом случае, я уже ответил на вопрос :-p - person gniourf_gniourf; 11.12.2012
comment
@gniourf_gniourf о, классно; никогда раньше не использовал prinf, поэтому сегодня узнал кое-что новое =) - person sampson-chen; 11.12.2012