harangzsolt33 has asked for the wisdom of the Perl Monks concerning the following question:

I don't know where to post this question as it's somewhat off-topic. But seriously, I am trying to figure this out and AI wasn't able to help me. And I have tried to fool with this for hours to no use. So, I'm about to give up.

I saw a regex here on PerlMonks awhile back that took a string and inserts commas into numbers. It's pretty amazing how that works, and I am just now beginning to grasp why and how it works. But now I would like to port it to bash. Now, of course, bash doesn't have regex search and replace but sed does. So, when I plugged this into sed, it complains and says "sed: -e expression #1, char 39: Invalid preceding regular expression" (I'm using sed GNU v4.9 and bash 5.2.15 x64)

WHAT IS WRONG???

Original perl code: #!/usr/bin/perl -w use strict; use warnings; # I copied this regex from: # www.PerlMonks.org/?node_id=157725 # Usage: STRING = Commify(STRING) # sub Commify { defined $_[0] or return ''; my $N = reverse $_[0]; $N =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $N; } print Commify('Testing 1234 testing... -123456789.01234567800 testing +test 4,500,000.00000');

And now the BASH script:

#!/bin/bash # This function inserts commas into numbers # at every 3 digits and returns the result # in a global variable called $STR. # function Commify { # First, reverse the string STR=$(echo "$1" | rev) STR=$(echo "$STR" | sed -E 's/([0-9]{3})(?=[0-9])(?![0-9]*\.)/\1,/g' +) # Now reverse it back: STR=$(echo "$STR" | rev) } Commify 'Testing 1234 testing... -123456789.01234567800 testing test 4 +,500,000.00000' echo $STR

Replies are listed 'Best First'.
Re: Commify function regex in Perl vs sed (off-topic sed)
by LanX (Saint) on Sep 20, 2025 at 23:14 UTC
    > I don't know where to post this question as it's somewhat off-topic

    certainly not a PM discussion, I moved it to SOPW and marked it as off-topic.

    > WHAT IS WRONG???

    The trick in Formatting numbers with commas is to reverse the string of numbers to operate from right to left and to use positive and negative look-aheads

    • (?!\d*\.) means anything prior to a decimal dot is ignored
    • (\d\d\d)(?=\d) means only three digits which are followed by another digit will be separated by a comma

    Regarding SED: to my knowledge are neither look-ahead nor look-behind supported.

    This

    > "sed: -e expression #1, char 39: Invalid preceding regular expression"

    should have been obvious.

    The normal recommendation when sed fails is to use Perl instead. (yes seriously)

    > I am trying to figure this out and AI wasn't able to help me. And I have tried to fool with this for hours to no use.

    which proves the current wisdom that LLMs are only useful when used by someone with more expertise in the matter. (no insult intended¹)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    ¹) but LOL²

    ²) SCNR in your case :-)

      I came upon a page that talks about tricking sed into doing what I want, but the problem is that I don't understand the document. It's too advanced (for me).
      https://learnbyexample.github.io/sed-lookarounds/

      Anyway, I finally found a solution, and it was with the help of AI... which of course probably just found this line from some website:

      echo "$NUM" | sed -r ':a;s/^([-+]?[0-9]+)([0-9]{3})/\1,\2/;ta'

      And I wrote the following enclosure:

      # This function inserts commas into numbers at every 3 digits # and returns the result in a global variable called $STR. # function Commify { STR="$1" if [[ "$STR" =~ ([\+\-\$\(0-9\.]+) ]]; then local NEG='' local NUM="${BASH_REMATCH[1]}" local PREFIX="${STR%$NUM*}" local SUFFIX="${STR##*$NUM}" if [[ "$NUM" == *"+"* ]]; then NEG="+"; fi if [[ "$NUM" == *"-"* ]]; then NEG="-"; fi if [[ "$NUM" =~ 0+([1-9]+[0-9.]*) ]]; then NUM=${BASH_REMATCH[1]}; + fi NUM="$NEG$NUM" NUM=$(echo "$NUM" | sed -r ':a;s/^([-+]?[0-9]+)([0-9]{3})/\1,\2/;t +a') STR="$PREFIX$NUM$SUFFIX" fi }

      I could possibly put this in a while loop and make it commify every number in a string, but I think this is fine as it is. Let's not overcomplicate things too much... Btw I can't help but wonder how amazingly similar Bash scripting is to Perl. There are soo many similarities!

        > just found this line from some website:

        > echo "$NUM" | sed -r ':a;s/^([-+]?[0-9]+)([0-9]{3})/\1,\2/;ta'

        IMHO not the same thing, but if you're happy... 🤷🏻‍♂️

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: Commify function regex in Perl vs sed
by eyepopslikeamosquito (Archbishop) on Sep 21, 2025 at 08:03 UTC

    > But now I would like to port it to bash ... bash doesn't have regex search and replace but sed does

    Why do you want to port it to bash?

    The wise coder won't shell out to grep, awk or sed
    Since no JAPH would be seen doing that, dead

    -- from hippo's classic poem The wise coder

    Unix shell versus Perl argues strongly that it's better to write everything in Perl, rather than a motley mix of shell, grep, sed, awk and so on -- so please tell us why you want to port a working perl script to bash.

    I've added your question to my list of Shell to Perl References.

    👁️🍾👍🦟
Re: Commify function regex in Perl vs sed
by sleet (Monk) on Sep 21, 2025 at 07:08 UTC
    why not:
    $ printf "%'f" -987654321.12345 -987,654,321.123450
      Nota bene:
      • this is a GNU util not Perl's printf
      • it depends on locale.
      I needed a prior export LC_NUMERIC=en_US.utf8 to make this work, otherwise the decimal point is causing an invalid number error.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      see Wikisyntax for the Monastery