Is it possible to format currency in Bash?
Example data is received as
19366
Data to be displayed as
$193,66
Thanks.
CodePudding user response:
Pseudo floating poing using integer as strings
I often use this kind of pseudo float:
amount=123456
amount=00$amount # avoid bad length error
printf '$%.2f\n' ${amount::-2}.${amount: -2}
$1234.56
for amount in 0 1 12 123 1234 12345;do
amount=00$amount
printf '$%.2f\n' ${amount::-2}.${amount: -2}
done
$0.00
$0.01
$0.12
$1.23
$12.34
$123.45
As a function:
int2amount() {
if [[ $1 == -v ]]; then
local -n _out="$2"
shift 2
else
local _out
fi
local _amount=00$(($1))
printf -v _out $'$%\47.2f' ${_amount::-2}.${_amount: -2}
[[ ${_out@A} != _out=* ]] || echo "$_out"
}
Then
int2amount 123456
$1’234.56
int2amount -v var 1234567
echo $var
$12’345.67
Remark regarding locale, decimal separator and thousand separators
In your request, your radix mark is a coma ,
. This depend on your locale configuration. U could hit something like:
set | grep ^LC\\\|^LANG
to show how this is configured on your host.
Try:
for locvar in C en_US.UTF-8 de_DE.UTF-8 ;do
LC_NUMERIC=$locvar int2amount 1234567
done
$12345.67
$12,345.67
bash: line 1: printf: 0012345.67: invalid octal number
$12.345,00
Error because unsing de_DE
locale configuration, you have to use a coma as separator (Decimal separator at wikipedia).
This is already know to produce issues using bc
: How do I change the decimal separator in the printf command in bash?
Final function unsing variable decimal separator
int2amount () {
local TIMEFORMAT=%U _decsep
read _decsep < <(eval 'time true' 2>&1)
_decsep=${_decsep//[0-9]}
if [[ $1 == -v ]]; then
local -n _out="$2"
shift 2
else
local _out
fi
local _amount=00$(($1))
printf -v _out '$%'\''.2f' ${_amount::-2}${_decsep}${_amount: -2}
[[ ${_out@A} != _out=* ]] || echo "$_out"
}
for locvar in C en_US.UTF-8 de_DE.UTF-8 ;do
LC_NUMERIC=$locvar int2amount 1234567
done
$12345.67
$12,345.67
$12.345,67
Note about LC_ALL
: If in your environment, a variable $LC_ALL
is defined, all demos using LC_NUMERIC
won't work because LC_ALL
is over. You have to unset LC_ALL
or use:
LC_ALL=$locvar LC_NUMERIC=$locvar int2amount 1234567
in last demo.
CodePudding user response:
Simply handle your value as a text string, instead of a number, and insert a dollar sign and a comma at the correct positions:
$ v=19366
$ printf '$%s,%s\n' "${v:0: -2}" "${v: -2}"
$193,66
${v:offset:length)
expands as the substring of $v
that starts at character offset
(counting from 0
) and which length is length
. But negative offsets and lengths can be used to refer to the end of the string.
${v:0:-2}
expands as the substring of $v
that starts at the beginning (0
) and which length is the number of remaining characters minus two (-2
). In our example this is 193
.
${v: -2}
expands as the substring of $v
that starts two characters before the end (-2
) and which length (not specified) is the number of remaining characters. In our example this is 66
. Note the space between :
and -2
, it is needed to avoid another interpretation by the shell (providing default value 2
if v
is unset or null).
CodePudding user response:
You can use printf
amount="240570.578"
printf "%'.2f\n" $amount
> 240,570.58
CodePudding user response:
printf
does have a thousands grouping format specifier flag, however the character used to denote groups (monetary grouping character) dependens on locale (LC_NUMERIC
).
The C
or POSIX
locale uses no monetary grouping character. Therefore you can't do this portably with printf.
printf "%'d\n" 19366
Works if the current locale supports the comma monetary grouping character.
In my bashrc, I use the following function to add thousands groupings to any integer, using comma (,
) and preserving a non numeric prefix (like $
, or -
for negative numbers). It doesn't depend on locale, but does require rev
.
commafy ()
{
printf %s "${1%%[0-9]*}"
printf '%s\n' "${1##*[!0-9]}" |
rev |
sed -E 's/[0-9]{3}/&,/g; s/,$//' |
rev
}
Example:
commafy '$19366'
# gives
$19,366
You could slightly simplify this too:
printf %s \$
printf '%s\n' 19366 |
rev |
sed -E 's/[0-9]{3}/&,/g; s/,$//' |
rev