Respostas:
Com sed
:
$ echo "123456789" | sed 's/\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)/\1,\2,\3/g'
123,456,789
(Observe que isso funciona apenas para exatamente 9 dígitos!)
ou isso com sed
:
$ echo "123456789" | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta'
123,456,789
Com printf
:
$ LC_NUMERIC=en_US printf "%'.f\n" 123456789
123,456,789
echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}'
sed
só funciona se o número tiver exatamente 9 dígitos. O printf
não funciona no zsh. Portanto, a segunda sed
resposta é provavelmente a melhor.
echo 123456789 | awk '{printf ("%'\''d\n", $0)}'
(o que, evidentemente, nem sempre funciona no Linux, mas funciona bem no AIX e Solaris!?)
bash
's printf
suportes praticamente tudo que você pode fazer na printf
função C
type printf # => printf is a shell builtin
printf "%'d" 123456 # => 123,456
printf
de coreutils fará o mesmo
/usr/bin/printf "%'d" 1234567 # => 1,234,567
zsh
, post atualizado aqui .
vsnprintf
. Em um sistema GNU / Linux, o glibc parece suportá-lo desde, pelo menos, 1995.
export LC_NUMERIC="en_US"
se quiser forçar vírgulas.
locale -a
. Eu tive que usaren_US.utf8
Você pode usar numfmt:
$ numfmt --grouping 123456789
123,456,789
Ou:
$ numfmt --g 123456789
123,456,789
Observe que o numfmt não é um utilitário POSIX, ele faz parte do GNU coreutils.
-d, --grouping
uma vez que as hifenizações duplas precisam de opções longas?
--g
funciona bem para mim em vez de --grouping
, ou seja, numfmt --g 1234567890
e numfmt --grouping 1234567890
faz a mesma coisa. É um pequeno utilitário muito útil.
cat <<'EOF' |
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
EOF
perl -wpe '1 while s/(\d+)(\d\d\d)/$1,$2/;'
produz:
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
Isso é feito dividindo a sequência de dígitos em 2 grupos, o grupo da direita com 3 dígitos, o grupo da esquerda com o que resta, mas com pelo menos um dígito. Então tudo é substituído pelos 2 grupos, separados por vírgula. Isso continua até a substituição falhar. As opções "wpe" são para listagem de erros, incluem a declaração dentro de um loop com uma impressão automática e usam o próximo argumento como o "programa" perl (consulte o comando perldoc perlrun para obter detalhes).
Muitas felicidades ... felicidades, drl
BASH
/ AWK
alternativa para que ele não pode ter usado PERL
antes. De qualquer forma, é melhor explicar o que o comando faz - especialmente para as frases de uso único.
Com algumas awk
implementações:
echo "123456789" | awk '{ printf("%'"'"'d\n",$1); }'
123,456,789
"%'"'"'d\n"
é: "%
(aspas simples) (aspas duplas) (aspas simples) (aspas duplas) (aspas simples) d \ n"
Isso usará o separador de milhar configurado para o seu código do idioma (normalmente ,
em locais do inglês, espaço em francês, .
em espanhol / alemão ...). O mesmo que retornado porlocale thousands_sep
Um caso de uso comum para mim é modificar a saída de um pipeline de comandos para que os números decimais sejam impressos com mil separadores. Em vez de escrever uma função ou script, prefiro usar uma técnica que eu possa personalizar rapidamente para qualquer saída de um pipeline Unix.
Eu descobri printf
(fornecido pela Awk) a maneira mais flexível e memorável de conseguir isso. O caractere de apóstrofo / aspas simples é especificado pelo POSIX como um modificador para formatar números decimais e tem a vantagem de reconhecer o código de idioma e não se restringir ao uso de caracteres de vírgula.
Ao executar comandos do Awk a partir de um shell Unix, pode haver dificuldades para inserir um caractere de aspas simples dentro de uma string delimitada por aspas simples (para evitar a expansão de variáveis posicionais, por exemplo, shell $1
). Nesse caso, acho que a maneira mais legível e confiável de inserir o caractere de aspas simples é inseri-lo como uma sequência de escape octal (começando com \0
).
Exemplo:
printf "first 1000\nsecond 10000000\n" |
awk '{printf "%9s: %11\047d\n", $1, $2}'
first: 1,000
second: 10,000,000
Saída simulada de um pipeline mostrando quais diretórios estão usando mais espaço em disco:
printf "7654321 /home/export\n110384 /home/incoming\n" |
awk '{printf "%22s: %9\047d\n", $2, $1}'
/home/export: 7,654,321
/home/incoming: 110,384
Outras soluções estão listadas em Como escapar de uma única citação no awk .
Nota: conforme advertido em Imprimir uma cotação , é recomendável evitar o uso de seqüências de escape hexadecimais, pois elas não funcionam de maneira confiável em diferentes sistemas.
\047
.
awk
e bash
tenha boas soluções internas, com base em printf
, conforme descrito nas outras respostas. Mas primeiro sed
.
Pois sed
, precisamos fazer isso "manualmente". A regra geral é que, se você tiver quatro dígitos consecutivos, seguidos por um não dígito (ou final de linha), uma vírgula deverá ser inserida entre o primeiro e o segundo dígito.
Por exemplo,
echo 12345678 | sed -re 's/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/'
irá imprimir
12345,678
Obviamente, precisamos continuar repetindo o processo, para continuar adicionando vírgulas suficientes.
sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '
Em sed
, o t
comando especifica um rótulo que será saltado para se o último s///
comando for bem-sucedido. Por isso, defino um rótulo com :restart
, para que ele salte para trás.
Aqui está uma demonstração do bash (em ideone ) que funciona com qualquer número de dígitos:
function thousands {
sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '
}
echo 12 | thousands
echo 1234 | thousands
echo 123456 | thousands
echo 1234567 | thousands
echo 123456789 | thousands
echo 1234567890 | thousands
$ echo 1232323 | awk '{printf(fmt,$1)}' fmt="%'6.3f\n"
12,32,323.000
Se você está procurando números grandes, não consegui fazer as soluções acima funcionarem. Por exemplo, vamos obter um número realmente grande:
$ echo 2^512 |bc -l|tr -d -c [0-9]
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
Nota: preciso tr
remover a saída de nova linha com barra invertida do bc. Esse número é muito grande para ser tratado como um número flutuante ou de bit fixo no awk, e eu nem quero criar uma regexp grande o suficiente para dar conta de todos os dígitos no sed. Em vez disso, posso revertê-lo e colocar vírgulas entre grupos de três dígitos e depois revertê-lo:
echo 2^512 |bc -l|tr -d -c [0-9] |rev |sed -e 's/\([0-9][0-9][0-9]\)/\1,/g' |rev
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
awk: run time error: improper conversion(number 1) in printf("%'d
.
a="13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096"
echo "$a" | rev | sed "s#[[:digit:]]\{3\}#&,#g" | rev
13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096
sed 's/^,//g'
.
Eu também queria que a parte após o separador decimal fosse separada / espaçada corretamente, por isso escrevi esse script sed que usa algumas variáveis de shell para se ajustar às preferências regionais e pessoais. Também leva em consideração convenções diferentes para o número de dígitos agrupados :
#DECIMALSEP='.' # usa
DECIMALSEP=',' # europe
#THOUSSEP=',' # usa
#THOUSSEP='.' # europe
#THOUSSEP='_' # underscore
#THOUSSEP=' ' # space
THOUSSEP=' ' # thinspace
# group before decimal separator
#GROUPBEFDS=4 # china
GROUPBEFDS=3 # europe and usa
# group after decimal separator
#GROUPAFTDS=5 # used by many publications
GROUPAFTDS=3
function digitgrouping {
sed -e '
s%\([0-9'"$DECIMALSEP"']\+\)'"$THOUSSEP"'%\1__HIDETHOUSSEP__%g
:restartA ; s%\([0-9]\)\([0-9]\{'"$GROUPBEFDS"'\}\)\(['"$DECIMALSEP$THOUSSEP"']\)%\1'"$THOUSSEP"'\2\3% ; t restartA
:restartB ; s%\('"$DECIMALSEP"'\([0-9]\{'"$GROUPAFTDS"'\}\'"$THOUSSEP"'\)*\)\([0-9]\{'"$GROUPAFTDS"'\}\)\([0-9]\)%\1\3'"$THOUSSEP"'\4% ; t restartB
:restartC ; s%\([^'"$DECIMALSEP"'][0-9]\+\)\([0-9]\{'"$GROUPBEFDS"'\}\)\($\|[^0-9]\)%\1'"$THOUSSEP"'\2\3% ; t restartC
s%__HIDETHOUSSEP__%\'"$THOUSSEP"'%g'
}
Uma solução bash
/ awk
(conforme solicitado) que funciona independentemente do tamanho do número e usa ,
independentemente da thousands_sep
configuração da localidade , e onde quer que os números estejam na entrada e evita adicionar o separador de milhar depois em 1.12345
:
echo not number 123456789012345678901234567890 1234.56789 |
awk '{while (match($0, /(^|[^.0123456789])[0123456789]{4,}/))
$0 = substr($0, 1, RSTART+RLENGTH-4) "," substr($0, RSTART+RLENGTH-3)
print}'
Dá:
not number 123,456,789,012,345,678,901,234,567,890 1,234.56789
Em awk
implementações como mawk
essa, não há suporte para os operadores de regex com intervalo, altere o regexp para/(^|[^.0123456789])[0123456789][0123456789][0123456789][0123456789]+/
echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")'