Solución:
$ printf "%'.3fn" 12345678.901
12,345,678.901
tl; dr
-
Usar
numfmt
, si ÑU Las utilidades están disponibles, como en Linux de forma predeterminada:numfmt --grouping 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
-
De lo contrario, usar
printf
con el'
bandera de campo envuelta en un función de shell ese conserva el número de posiciones decimales de entrada (no codifica el número de producción lugares decimales).groupDigits 12343423455.23353 # -> 12,343,423,455.23353 in locale en_US
- Consulte la parte inferior de esta respuesta para obtener la definición de
groupDigits()
, que también es compatible múltiple números de entrada.
-
Alternativas ad-hoc que involucran subcapas ese también conserva el número de posiciones decimales de entrada (asume que la marca decimal de entrada es
.
o,
):- Una variante modular, pero algo ineficiente que acepta el número de entrada a través de stdin (y por lo tanto también se puede utilizar con entrada de canalización):
(n=$(</dev/stdin); f=${n#*[.,]}; printf "%'.${#f}fn" "$n") <<<12343423455.23353
- Alternativa significativamente más rápida, pero menos modular, que utiliza variable intermedia
$n
:n=12343423455.23353; (f=${n#*[.,]} printf "%'.${#f}fn" "$n")
- Una variante modular, pero algo ineficiente que acepta el número de entrada a través de stdin (y por lo tanto también se puede utilizar con entrada de canalización):
-
Alternativamente, considere el uso de mi Linux / macOS
grp
CLI (instalable connpm install -g grp-cli
):grp -n 12343423455.23353
En todos los casos hay advertencias; vea abajo.
La respuesta de Ignacio Vázquez-Abrams contiene el puntero crucial para usar con printf
: los '
bandera de campo (siguiendo el %
) formatea un número con el separador de miles de la configuración regional activa:
- Tenga en cuenta que
man printf
(man 1 printf
) no contiene esta información en sí: el utilidad / shell incorporadoprintf
en última instancia llama al función de bibliotecaprintf()
, y soloman 3 printf
ofrece una imagen completa con respecto a los formatos compatibles. - Variables de entorno
LC_NUMERIC
e, indirectamente,LANG
oLC_ALL
controlar la configuración regional activa con respecto al formato de números. - Ambos
numfmt
yprintf
respetar la configuración regional activa, tanto con respecto al separador de miles como a la marca decimal (“punto decimal”). - Usando solo
printf
por sí mismo, como en la respuesta de Ignacio, requiere que usted Código difícil el número de producción lugares decimales, en lugar de conservar la cantidad de lugares decimales que tenga la entrada; es esta limitación la quegroupDigits()
a continuación supera. -
printf "%'.<numDecPlaces>f"
tiene una ventaja sobrenumfmt --grouping
, sin embargo:-
numfmt
solo acepta decimal números, mientras queprintf
‘s%f
también acepta hexadecimal enteros (p. ej.,0x3e8
) y números en decimal notación cientifica (p.ej,1e3
).
-
Advertencias
-
Locales sin agrupar: Algunas configuraciones regionales, en particular
C
yPOSIX
, por definición NO aplica agrupación, por lo que el uso de'
no tiene ningún efecto en ese evento. -
Incoherencias de la configuración regional del mundo real entre plataformas:
(LC_ALL='de_DE.UTF-8'; printf "%'.1fn" 1000) # SHOULD yield: 1.000,0
-
Linux: rendimientos
1.000,0
, como se esperaba. -
macOS / BSD: Cede inesperadamente
1000,0
– NO agrupación (!).
-
Formato de número de entrada: Cuando pasa un número a
numfmt
oprintf
, eso:- no debe ya contiene agrupación de dígitos
- debe ya utilizo el activo marca decimal de la configuración regional
- Por ejemplo:
(LC_ALL='lt_LT.UTF-8'; printf "%'.1fn" 1000,1) # -> '1 000,1'
- OK: el número de entrada no está agrupado y usa la marca decimal lituana (coma).
-
Portabilidad: POSIX no exigir los
printf
utilidad (a diferencia de la Cprintf()
función de biblioteca) para admitir caracteres de formato de punto flotante como%f
, dado que POSIX[-like] las conchas son solo números enteros; en la práctica, sin embargo, no conozco ningún shell / plataforma que no lo haga. -
Redondeo de errores y desbordamiento:
- Cuando usas
numfmt
yprintf
como se describe, se produce una conversión de ida y vuelta (cadena -> número -> cadena), que está sujeta a errores de redondeo; en otras palabras: reformatear con agrupación de dígitos puede llevar a un número diferente. - Usando carácter de formato
f
para emplear valores de coma flotante de doble precisión IEEE-754, solo hasta 15 dígitos significativos (los dígitos independientemente de la ubicación de la marca decimal) son garantizado para conservarse con precisión (aunque para números específicos puede funcionar con más dígitos). En la práctica,numfmt
y ÑUprintf
puede manejar con precisión más que eso; vea abajo. Si alguien sabe cómo y por qué, hágamelo saber. - Con demasiados dígitos significativos o un valor demasiado grande, el el comportamiento difiere entre
numfmt
yprintf
en general, y Entreprintf
implementaciones en plataformas; por ejemplo:
- Cuando usas
numft
:
[Fixed in coreutils 8.24, according to @pixelbeat] Comenzando con 20 dígitos significativos, el valor se desborda silenciosamente (!), Presumiblemente un error (a partir de GNU coreutils 8.23):
# 20 significant digits cause quiet overflow:
$ (fractPart=0000000000567890; num="1000.${fractPart}"; numfmt --grouping "$num")
-92.23372036854775807 # QUIET OVERFLOW
Por el contrario, un número demasiado grande lo hace generar un error por defecto.
printf
:
Linux printf
maneja hasta 20 dígitos significativos precisamente, mientras que la implementación de BSD / macOS está limitada a 17:
# Linux: 21 significant digits cause rounding error:
$ (fractPart=00000000005678901; num="1000.${fractPart}"; printf "%'.${#fractPart}fn" "$num")
1,000.00000000005678902 # ROUNDING ERROR
# BSD/macOS: 18 significant digits cause rounding error:
$ (fractPart=00000000005678; num="1000.${fractPart}"; printf "%'.${#fractPart}fn" "$num")
1,000.00000000005673 # ROUNDING ERROR
La versión de Linux nunca parece desbordarse, mientras que la versión BSD / macOS informa un error con números demasiado grandes.
Función de shell Bash groupDigits()
:
# SYNOPSIS
# groupDigits num ...
# DESCRIPTION
# Formats the specified number(s) according to the rules of the
# current locale in terms of digit grouping (thousands separators).
# Note that input numbers
# - must not already be digit-grouped themselves,
# - must use the *current* locale's decimal mark.
# Numbers can be integers or floats.
# Processing stops at the first number that can't be formatted, and a
# non-zero exit code is returned.
# CAVEATS
# - No input validation is performed.
# - printf(1) is not guaranteed to support non-integer formats by POSIX,
# though not doing so is rare these days.
# - Round-trip number conversion is involved (string > double > string)
# so rounding errors can occur.
# EXAMPLES
# groupDigits 1000 # -> '1,000'
# groupDigits 1000.5 # -> '1,000.5'
# (LC_ALL=lt_LT.UTF-8; groupDigits 1000,5) # -> '1 000,5'
groupDigits() {
local decimalMark fractPart
decimalMark=$(printf "%.1f" 0); decimalMark=${decimalMark:1:1}
for num; do
fractPart=${num##*${decimalMark}}; [[ "$num" == "$fractPart" ]] && fractPart=""
printf "%'.${#fractPart}fn" "$num" || return
done
}