skip to Main Content

I have a QNAP NAS with a modified Debian OS. The shell is using GNU bash, version 3.2.57(1)-release (x86_64-QNAP-linux-gnu). I have a number that I need to reformat to add thousand separators.

I used printf with the ‘.d parameter to set the thousands and forgot about % in CRON so I did something different and this works.

printf "This works $nl" | sed ':a;s/\B[0-9]{3}>/,&/;ta'

However, I now need to do further formatting on it by adding spaces to the front to make it easier reading. I have an input file with number that need format in thousand and also padded at the front.

12345 02-01-2024 root reallylongpasswordthatissecure
9999 01-01-2025 admin anotherreallylongpassword
300 01-01-2025 user 123456

I want the output file to convert all the lines, couple of dozen to

12,345 02-01-2024 root password
9,999 01-01-2025 admin password
300 01-01-2025 user 123456

I can’t feed in the sed line as that will convert any passwords as well if the password is something like 123456. So I want to convert the first column only to have thousand separators and front padding to 10 characters whilst leaving the rest.

I can get it to do the conversion for the thousand delimiters as above OR I can get it to do the padding with bash.

There must be a way to do this with sed. Sed looks like it can do it but is so complex that I have no idea how to do it.


I have played with sed to no avail.
I get

12,345 02-01-2024 root password
9,999 01-01-2025 admin password
300 01-01-2025 user 123456

OR

 12345 02-01-2024 root password
   9999 01-01-2025 admin password
      300 01-01-2025 user 123456

I have written a bash script to extract the column and perform the conversion then simply append the rest on. That works fine but with the delimiters and padding it is 30 lines long and the rest of the script, the main part, is only six. It looks horribly inefficient and clumsy.

2

Answers


  1. If you can use awk it is fairly easy:

    LC_ALL=en_US.UTF-8 awk '
    { 
        s=$1
        sub(/^[^ ]+ /,"",$0)
        printf("%'"'"'d %sn", s, $0) 
    }' your_file 
    

    With your example, prints:

    12,345 02-01-2024 root reallylongpasswordthatissecure
    9,999 01-01-2025 admin anotherreallylongpassword
    300 01-01-2025 user 123456
    

    You can also use GNU sed:

    gsed -E ':a;s/^([0-9]+)([0-9]{3})b/1,2/;ta' your_file    
    #same
    
    Login or Signup to reply.
  2. Assumptions:

    • we only want to convert the first space-delimited field
    • the first field will always be an integer (eg, we do not have to worry about floats or numbers that already have a thousands separator applied)
    • we are to left pad the new number with spaces for a total length of 10 characters (including the separator(s)); this also means the integer will be no more than 8 digits (otherwise we’ll end up with an output that’s more than 10 characters in length)
    • we are not to modify the password field (as displayed in OP’s various sets of output)

    One bash idea using a while/read loop and a printf call:

    while read -r fld1 rest_of_line
    do
        LC_ALL=en_US.UTF-8 printf "%'10d %sn" "${fld1}" "${rest_of_line}"
    done < file.txt
    

    NOTES:

    • whether or not you need the LC_ALL=en_US.UTF-8 depends on your default character set
    • if you’re using a different character set that provides the thousands separator then feel free to replace en_US.UTF-8 as needed

    This generates:

        12,345 02-01-2024 root reallylongpasswordthatissecure
         9,999 01-01-2025 admin anotherreallylongpassword
           300 01-01-2025 user 123456
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search