33

Say I have a POSIX shell script that

  1. needs to run on different systems/environments that I do not control, and
  2. needs to remove the decimal separator from a string that is emitted by a program that respects the locale settings.

How can I detect the decimal separator in the most general way?

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
gboffi
  • 1,220
  • 1
  • 14
  • 30
  • What sort of script ? Where is it running ? Please update your question, so that it can be answered. – X Tian Sep 12 '19 at 09:45
  • @XTian A generic shell script, that's running on a Unix/Linux system. ֎   I deliberately posed the question in the most general way because I'm interested in the most general answer, as I specified in the last line of my question. – gboffi Sep 12 '19 at 09:49
  • 1
    @StéphaneChazelas A POSIX script! Honest, I forgot that i used to use `tcsh` in ..., oh my, you definitely can say that I'm OLD!!! – gboffi Sep 12 '19 at 11:04
  • 2
    Can't you run the string-emitted program under a `LC_ALL=C` environment? – Ángel Sep 12 '19 at 22:36
  • @Ángel Oh yes, of course I could... – gboffi Sep 13 '19 at 06:02

2 Answers2

50

Ask locale:

locale decimal_point

This will output the decimal point using the current locale settings.

If you need the thousands separator:

locale thousands_sep

You can view all the numeric keywords by requesting the LC_NUMERIC category:

locale -k LC_NUMERIC
Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
  • Is there a way to have the number directly formatted using the current locale's decimal/thousands separators? – muru Sep 12 '19 at 10:57
  • 3
    @muru `printf "%'f"` would do it, for `printf` implementations which support `%f`. – Stephen Kitt Sep 12 '19 at 11:05
  • 2
    @muru The only _builtin_ that I know of, that can output a decimal separator, is `printf` and some shell (e.g., `dash`) do not support internationalized output. In another answer Stéphane Chazelas explained, in a [comment](https://unix.stackexchange.com/questions/541342/which-is-the-current-decimal-separator#comment1003598_541355), that it's not required by POSIX – gboffi Sep 12 '19 at 11:18
6

If that's a zsh shell script, you can use the $langinfo special associative array in the zsh/langinfo module:

zmodload zsh/langinfo
radix=$langinfo[RADIXCHAR]

(that maps to the standard nl_langinfo(RADIXCHAR), see man nl_langinfo on your system for details; $langinfo[THOUSEP] for the thousand separator).

In a bash script (would also work in zsh), you should be able to get it without forking a separate process using the printf builtin:

printf -v radix %.1f 1 && radix=${radix:1:1}

To convert a number from the user's locale format to the C locale format, with the ksh93 shell, you could do it like:

$ locale title
German locale for Germany
$ x=1.123.456,78 ksh -c 'typeset -F x="$x"; LC_ALL=C; printf "%.23g\n" "$x"'
1123456.78
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • Most generic, could it be? `tmp=$(printf %.1f 0);tmp=${tmp#0};radix=${tmp%0}` – gboffi Sep 12 '19 at 10:55
  • 1
    @gboffi, that would work in some internationalized `printf` implementations that support `%f`, but not all. `%f` support is not mandated by POSIX. The `printf` of `dash` for instance always uses `.` – Stéphane Chazelas Sep 12 '19 at 10:57
  • re `dash` not internationalized... I've just found that... so the most general solution is resorting to `locale decimal_point`, isn't it? – gboffi Sep 12 '19 at 11:01
  • @gboffi, probably. GNU `awk` can also interpret numbers in the current locale with when in POSIX mode (doesn't handle the thousand separator though). – Stéphane Chazelas Sep 12 '19 at 11:09