In python

 re.sub(r"(?<=.)(?=(?:...)+$)", ",", stroke ) 

To split a number by triplets, e.g.:

 echo 123456789 | python -c 'import sys;import re; print re.sub(r"(?<=.)(?=(?:...)+$)", ",",  sys.stdin.read());'
 123,456,789

How to do the same with bash/awk?

10 Answers 10

up vote 19 down vote accepted

With sed:

$ echo "123456789" | sed 's/\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)\([[:digit:]]\{3\}\)/\1,\2,\3/g'
123,456,789

(Note that this only works for exactly 9 digits!)

or this with sed:

$ echo "123456789" | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta'
123,456,789

With printf:

$ LC_NUMERIC=en_US printf "%'.f\n" 123456789
123,456,789
  • I'm also trying with awk but it's add comma at the last echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g")' – Rahul Patil Feb 6 '14 at 7:56
  • now I get but it's seems complex echo 123456789 | awk '$0=gensub(/(...)/,"\\1,","g"){sub(",$",""); print}' – Rahul Patil Feb 6 '14 at 8:07
  • 1
    That first sed only works if the number is exactly 9 digits. The printf doesn't work on zsh. Thus the second sed answer is probably the best. – Patrick Feb 6 '14 at 13:51
  • 1
    @RahulPatil That only works properly if the number of digits is a multiple of 3. Try with "12345678" and you'll see what I mean. – Patrick Feb 6 '14 at 13:52
  • 1
    You can do echo 123456789 | awk '{printf ("%'\''d\n", $0)}' (which evidently doesn't always work on Linux!?, but works fine on AIX and Solaris) – Johan Nov 28 '14 at 9:36

bash's printf supports pretty much everything you can do in the printf C function

type printf           # => printf is a shell builtin
printf "%'d" 123456   # => 123,456

printf from coreutils will do the same

/usr/bin/printf "%'d" 1234567   # => 1,234,567
  • This is now supported in zsh too, updated post here. – don_crissti Dec 13 '15 at 19:55
  • I'm on bash 4.1.2 and it doesn't support... :( – msb Jan 31 '17 at 19:19
  • @msb It seems to depend on your system's vsnprintf. On a GNU/Linux system, glibc appears to have supported it since at least 1995. – Mikel Feb 1 '17 at 1:50
  • 2
    Note printf uses the thousands separator for your current locale, which might be a comma, dot, or nothing at all. You can export LC_NUMERIC="en_US" if you want to force commas. – medmunds Mar 27 '17 at 18:31
  • Get list of supported locale's with locale -a. I had to use en_US.utf8 – eludom May 11 at 9:48
cat <<'EOF' |
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
EOF
perl -wpe '1 while s/(\d+)(\d\d\d)/$1,$2/;'

produces:

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

This is accomplished by splitting the string of digits into 2 groups, the right-hand group with 3 digits, the left-hand group with whatever remains, but at least one digit. Then everything is replaced by the 2 groups, separated by a comma. This continues until the substitution fails. The options "wpe" are for error listing, enclose the statement inside a loop with an automatic print, and take the next argument as the perl "program" (see command perldoc perlrun for details).

Best wishes ... cheers, drl

  • Thanks to anonymous for the feedback. Even a downvote can be useful, but only if explained -- please comment on what you saw that was wrong. Thanks ... cheers – drl May 21 at 15:21
  • I think the downvote here is because you did not explain what the command does. The OP asked for a BASH/AWK alternative so he may not have used PERL before. In any case, best to explain what the command does - especially so for one-liners. – AnthonyK Jun 3 at 2:42
  • @AnthonyK -- thanks for probable explanation. I added comments to briefly explain how it works. I think alternative solutions are often useful, but your point about possibly not having used perl is noted ... cheers – drl Jun 3 at 10:53
  • I tried the sed and python suggestions on this page. The perl script was the only one that worked for a whole file. The file was filed with text and numbers. – Mark Aug 24 at 14:23

awk and bash have good built-in solutions, based on printf, as described in the other answers. But first, sed.

For sed, we need to do it "manually". The general rule is that if you have four consecutive digits, followed by a non-digit (or end-of-line) then a comma should be inserted between the first and second digit.

For example,

echo 12345678 | sed -re 's/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/'

will print

12345,678

We obviously need to then keep repeating the process, in order to keep adding enough commas.

sed -re ' :restart ; s/([0-9])([0-9]{3})($|[^0-9])/\1,\2\3/ ; t restart '

In sed, the t command specifies a label that will be jumped to if the last s/// command was sucessful. I therefore define a label with :restart, in order that it jumps back.

Here is a bash demo (on ideone) that works with any number of digits:

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

With awk:

echo "123456789" | awk '{ printf("%'"'"'d\n",$1); }'  

123,456,789  

"%'"'"'d\n" is: "%(single quote)(double quote)(single quote)(double quote)(single quote)d\n"

You can use numfmt:

$ numfmt --grouping 123456789
123,456,789

Or:

$ numfmt --g 123456789
123,456,789

Note that numfmt is not a POSIX utility, it is part of GNU coreutils.

$ echo 1232323 | awk '{printf(fmt,$1)}' fmt="%'6.3f\n"
12,32,323.000

If you are looking at BIG numbers I was unable to make the above solutions work. For example, lets get a really big number:

$ echo 2^512 |bc -l|tr -d -c [0-9] 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096

Note I need the tr to remove backslash newline output from bc. This number is too big to treat as a float or fixed bit number in awk, and I don't even want to build a regexp large enough to account for all the digits in sed. Rather, I can reverse it and put commas between groups of three digits, then unreverse it:

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

I also wanted to have the part after the decimal separator correctly separated/spaced, therefore I wrote this sed-script which uses some shell variables to adjust to regional and personal preferences. It also takes into account different conventions for the number of digits grouped together:

#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'
}
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
  • That adds a spurious leading comma if the number of digits in the number is a multiple of 3. – Stéphane Chazelas Jun 3 at 8:09

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.