Вопрос или проблема
В Ubuntu 14.04.1 LTS 64-bit
bash я объявляю переменные с плавающей точкой, умножая переменные с плавающей точкой в bash через bc
, с установкой масштаба на 3; однако, я не могу сделать так, чтобы количество цифр после десятичной точки стало нулевым, и избавиться от нуля слева от десятичной точки. Как я могу преобразовать, скажем, 0.005000000
в .005
? Это необходимо из-за моей системы наименования файлов. Благодарю за рекомендации.
ОБНОВЛЕНИЕ: Могу ли я использовать это для уже определённых переменных оболочки и переопределять их? Следующий код выдаёт ошибку.
~/Desktop/MEEP$ printf "%.3f\n" $w
bash: printf: 0.005000: invalid number
0,000
Вывод locale
@vesnog:~$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=tr_TR.UTF-8
LC_TIME=tr_TR.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=tr_TR.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=tr_TR.UTF-8
LC_NAME=tr_TR.UTF-8
LC_ADDRESS=tr_TR.UTF-8
LC_TELEPHONE=tr_TR.UTF-8
LC_MEASUREMENT=tr_TR.UTF-8
LC_IDENTIFICATION=tr_TR.UTF-8
LC_ALL=
Вывод echo $w
@vesnog:~$ echo $w
0.005000
Простой способ — использовать printf
:
$ printf "%.3f\n" 0.005000000000
0.005
Чтобы удалить ведущий 0
, просто уберите его с помощью sed
:
$ printf "%.3f\n" 0.005000000000 | sed 's/^0//'
.005
Существует специальная переменная под названием scale
. Вы можете настроить эту переменную для ограничения точности.
ПРИМЕР
$ echo "300/7" | bc -l
42.85714285714285714285
Чтобы ограничить точность,
$ echo "scale=2; 300/7" | bc -l
42.85
ОБНОВЛЕНО
$ echo "scale=3; 300/7" | bc -l | sed 's/[0-9]*\././g'
.857
Эта команда sed удаляет конечные нули:
sed 's/\(0\.0*[1-9][1-9]*\)0*/\1/'
Она запоминает любые значения, начинающиеся с 0.
, за которыми следуют нули, за которыми идёт 1 или более цифр от 1 до 9, а затем ноль или более нулей. Затем она обрезает ноль или более конечных нулей.
Это позволяет удалить произвольное количество конечных нулей и сохранить любое произвольно малое число, большее нуля, независимо от того, насколько оно мало.
Например
0.054000
становится 0.054
, или 0.00000010000000
становится 0.0000001
и так далее.
Для получения дополнительной информации ознакомьтесь с замечательным руководством по sed на Grymoire.
Понимаю, что поздновато, но для всех новых людей, которые гуглят эту проблему.
Проблема с “недопустимым числом” не была решена в самом популярном ответе, фактически, ответ также выдаёт ошибку недопустимого числа.
Причина — переменная LC_NUMERIC
из locale. Простое решение: используйте “,” вместо “.” в качестве десятичной точки. Еще проще — измените локальную переменную.
Тест
export LC_NUMERIC="en_US.UTF-8"
Постоянно
echo 'export LC_NUMERIC="en_US.UTF-8"' >>~/.bashrc
Почему не grep?
echo "$your_number" | grep -o '.*[1-9]'
Есть также возможность использовать встроенный в Bash Параметр Замены
вместе с Расширенным Шаблоном Соответствия
в два шага.
Для данного массива чисел с плавающей точкой, мы можем сначала удалить любые ведущие нули, а затем ограничить количество цифр после десятичной точки:
#! /bin/bash
# Включаем расширенный шаблон соответствия
shopt -s extglob
floats=('0.000919104'
'0.1000919104'
'2.1000919104'
'12001.1000919104'
'0012001.1000919104'
'2001.1000919104'
'2001.011000919104'
'0.9011000919104'
'00.19011000919104'
'00.0019011000919104'
'006100.0019011000919104'
'.0019011000919104'
'.019011000919104'
'.19011000919104')
result=()
# Удаляем ведущие нули, обрабатываем каждое очищенное число с плавающей точкой и ограничиваем
# количество цифр после запятой, собирая результаты
for f in "${floats[@]##*(0)}"; do
result+=("${f%"${f##*.*(0)[^0]?}"}")
done
# Сравнение ввода и собранного вывода
printf '%s\n%s\n' 'INPUT:' "${floats[@]}"
printf '%s\n%s\n' 'OUTPUT:' "${result[@]}"
# Результат будет таким:
INPUT:
0.000919104
0.1000919104
2.1000919104
...
OUTPUT:
.00091
.10
2.10
12001.10
12001.10
2001.10
2001.011
.90
.19
.0019
6100.0019
.0019
.019
.19
Мы также можем удалять ведущие нули внутри цикла (по floats
), например, с помощью f="${f##*(0)}"
, чтобы избежать неявного двойного цикла.
Ограничение числа цифр после десятичной точки выполняется внутри цикла, для каждого элемента, через "${f%"${f##*.*(0)[^0]?}"}"
.
Это то, что я бы назвал “псевдо-вложенной Параметр Замены
“, когда из обрабатываемого числа с плавающей точкой удаляется подстрока справа налево, начинающаяся с произвольного количества символов, за которой следует разделитель плавающей точки ‘.’, за которым следуют (опционально) нули, одна не-ноль цифра и одна произвольная цифра.
Так как работа идет со строками на этом этапе, округление не применяется.
Ограничение цифр (здесь 2) после десятичной точки фактически определяется количеством использованных ?
соответствий, или 1+n
, если можно так выразиться.
Но обратите внимание, что этот метод будет иметь проблемы с числами с плавающей точкой, содержащими разделители тысяч (например, что-то вроде ‘2.401.360,3430’). Поэтому используйте только ‘,’ или что-нибудь, кроме ‘.’, в качестве разделителя плавающей точки.
.
Ответ или решение
Вопрос касается работы с плавающими числами в Bash на Ubuntu 14.04.1 LTS 64-bit, где основная проблема заключается в форматировании чисел для соответствия определенной схеме именования файлов. Пользователь пытается отформатировать плавающее число, например, 0.005000000
, чтобы оно выглядело как .005
, удалив ведущий нуль и лишние нули после значащих цифр. При этом возникает ошибка в команде printf
, связанная с конфигурацией locale
.
Теория
Bash не поддерживает плавающую точку нативно, что требует использования вспомогательных инструментов, таких как bc
(аритметика в терминале) для выполнения операций с плавающей точкой. bc
позволяет задать точность вычислений с помощью установки значения переменной scale
. С этой командой можно управлять количеством знаков после запятой, но это не решает проблемы с форматированием вывода в целях согласованности стиля именования файлов.
Появление ошибки, связанной с printf
, вероятно, обусловлено региональными настройками операционной системы, в частности переменной LC_NUMERIC
, которая управляет форматом чисел. Если этот параметр установлен на использование запятой как десятичного разделителя, printf
может не корректно интерпретировать число с точкой. Таким образом, вам нужно либо изменить locale
, либо учесть эту особенность при работе с данными.
Пример
Для иллюстрации можно использовать следующую комбинацию команд, чтобы прийти к желаемому формату числа:
-
Работа с
bc
для форматирования числа:number=$(echo "scale=3; your_expression" | bc -l)
Здесь
scale=3
задает точность до трех знаков после запятой. -
Исправление региональных настроек:
Проверьте ваши текущие настройки
locale
:locale
Если
LC_NUMERIC
отличается отen_US.UTF-8
, вы можете временно изменить это для текущей сессии:export LC_NUMERIC="en_US.UTF-8"
Для постоянного изменения добавьте:
echo 'export LC_NUMERIC="en_US.UTF-8"' >> ~/.bashrc
-
Форматирование вывода с помощью
sed
:После получения числа воспользуйтесь
sed
, чтобы удалить ненужные символы:formatted_number=$(echo $number | sed 's/^0//; s/\(.\d*[1-9]\)0*$/\1/')
Это удаляет ведущий ноль и обрезает лишние нули в конце.
Применение
Рассмотрим реальное применение этой техники для именования файлов. Если имя файла должно начинаться со значимого числа без ведущего нуля, использование этой методологии позволит соблюсти формат, избегая ошибок и приводя к нужному представлению. Например, если ваш исходный результат вычисления равен 0.005000
, переводы и преобразования помогут достичь варианта .005
.
Теперь, переходя к программному применению:
#!/bin/bash
# Процесс вычислений
w=$(echo "0.5/100" | bc -l)
# Исправление вывода и форматирование
result=$(echo $w | sed 's/^0//; s/\(.\d*[1-9]\)0*$/\1/')
echo "Итоговое отформатированное число: $result"
Этот скрипт отделяет результат вычислений, обрабатывает его через sed
и выводит число в задаваемой вами схеме именования
Применение данной методики гарантирует, что ваши файлы будут организованы и легко идентифицируемы по структурированным именам, что критично для эффективной работы и поддержания порядка в файловой системе вашего проекта.