0

Is it possible to format currency in Bash?

Example data is received as 19366 Data to be displayed as $193,66

Thanks.

Jason Aruni
  • 23
  • 1
  • 5

5 Answers5

3

Preamble In your request, you use coma , as decimal separator (radix mark). For locale support, see second part of my answer.

1. 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

2. 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.

As there are many issues regarding locales, I've asked How to determine which character is used as decimal separator or thousand separator under current locale as separated question.

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 () {
    if [[ $1 == -v ]]; then
        local -n _out="$2"
        shift 2
    else
        local _out
    fi
    local _amount=00$(($1)) _decsep
    printf -v _decsep %.1f 1
    _decsep=${_decsep:1: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.

F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
3

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).

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
1

You can use printf

amount="240570.578"
printf "%'.2f\n" $amount
> 240,570.58
Diego Velez
  • 1,584
  • 1
  • 16
  • 20
0

printf does have a thousands grouping format specifier flag, however the character used to denote the groups (non-monetary grouping character) depends on locale (LC_NUMERIC).

The C or POSIX locale uses no grouping character. Therefore you can't do this portably with printf.

printf "%'d\n" 19366

Works if the current locale supports the comma 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
dan
  • 4,846
  • 6
  • 15
0

Simplistically -

$: sed -E 's/([0-9]*)([0-9][0-9])$/$\1,\2/'<<<"19366"
$193,66
Paul Hodges
  • 13,382
  • 1
  • 17
  • 36