Q:

How to create dynamic substring with awk

Let say i have file like below.
ABC_DEF_G-1_P-249_8.CSV

I want to cut to be like this below.
ABC_DEF_G-1_P-249_

I use this awk command to do that like below.
ls -lrt |  grep -i .CSV | tail -1 | awk -F ' ' '{print $8}' | cut -c 1-18

Question is, if the number 1, is growing, how to make the substring is dynamic
example like below...
ABC_DEF_G-1_P-249_
....
ABC_DEF_G-10_P-249_
ABC_DEF_G-11_P-249_
...
ABC_DEF_G-1000_P-249_

A:

To display the file names of all .CSV without everything after the last underscore, you can do this:
for fname in *.CSV; do echo "${fname%_*}_"; done

This removes the last underscore and evertyhing that follows it (${fname%_*}), and then appends an underscore again. You can assign that, for example, to another variable.
For an example file list of
ABC_DEF_G-1_P-249_9.CSV
ABC_DEF_G-10_P-249_8.CSV
ABC_DEF_G-1000_P-249_4.CSV
ABC_DEF_G-11_P-249_7.CSV
ABC_DEF_G-11_P-249_7.txt

this results in
$ for fname in *.CSV; do echo "${fname%_*}_"; done
ABC_DEF_G-1_P-249_
ABC_DEF_G-10_P-249_
ABC_DEF_G-1000_P-249_
ABC_DEF_G-11_P-249_

A:

You can do this with just ls and grep
 ls -1rt | grep -oP ".*(?=_\d{1,}\.CSV)"

If you are concerned about the output of ls -1, as mentioned in the comments you can use find as well
find -type f -printf "%f\n" | grep -oP ".*(?=_\d{1,}\.CSV)"

Outputs:
 ABC_DEF_G-1_P-249
 ABC_DEF_G-1000_P-249_

This assumes you want everything except the _number.CSV, if it needs to be case insensitive then you can the -i flag to the grep. The \d{1,} allows for the number between _ and .CSV to grow from one to many digits. Also doing it this way you don't have to worry about if the number 1 in your example increases:

ABC_DEF_G-1_P-249

