0

I have list of files like below:

[root@ods1 backup]# ls -l
total 93892810
-rw-r----- 1 root root 651248 Feb 17 08:34  abc_def_g_17-02-2022.sql
-rw------- 1 root root 665248 Mar 23 08:46  bbc_23-03-2022.sql
-rw-r----- 1 root root 676992 Apr 04 16:52  zz_b_04-04-2022.sql

What i need to achieve is:

abc_def_g
bbc
zz_b

I tried to do this but it didn't work:

ls -l | awk '{print $9}' | cut -d '_' -f1-2

How can I achieve this goal? Any series of commands that get the lines until last _ character should do the job.

Edit: This commands are executed in CentOS 8.

6 Answers6

6

To not parse ls, you can use a simple loop and shell syntax:

for f in *; do echo "${f%_*}"; done

if you need to work further with that list (and you have a shell that is capable of using arrays), you can add the items to an array:

tokens=()
for f in *; do tokens=("${tokens[@]}" "${f%_*}"); done
echo "${tokens[@]}"

Or without the loop, directly declare the array:

files=(*)
tokens=("${files[@]%_*}")
#or
for token in "${files[@]%_*}"; do echo "$token"; done
pLumo
  • 22,565
2

You can try something like:

ls|awk -F_ '{NF-=1}1' OFS=_

Here the trick is to decrease number of fields by one (strip the last fiels)

Romeo Ninov
  • 17,484
  • 1
    Many thanks, it worked. – postgresnewbie Apr 21 '22 at 06:37
  • 2
    If you just need to obtain a list to read for humans and/or you control the input files, that option is fine. But files are allowed to have a \n included, so it might break in a script when you cannot control input files. – pLumo Apr 21 '22 at 06:44
  • That's how it really goes @pLumo (inputs are controlled by me), thanks for heads up anyways. – postgresnewbie Apr 21 '22 at 10:42
  • 1
    NF-=1 can also be written NF-- however note that not all awk implementations rebuild $0 upon modifying NF. With some, including the original awk implementation, you'd need to add a $1=$1 (with a difference in behaviour if the input doesn't contain _ characters). – Stéphane Chazelas May 06 '22 at 07:27
2

In the zsh shell:

print -r -C1 -- *_*.sql(Ne['REPLY=${REPLY%_*}'])

This uses a glob qualifier that modifies the names that match the given globbing pattern *_*.sql by removing everything after and including the last underscore. The actual substitution is made using a standard parameter substitution.

If you want to use the resulting strings to do something other than printing them, then use the globbing pattern, without the print, where that makes sense (e.g., in a for loop or as an argument to some other command). Don't put the print command from above in a command substitution!

Kusalananda
  • 333,661
1

First of all, do you need to do ls -l?  Why not just do ls, or printf '%s\n' *, to avoid generating the metadata (owner, size, date, etc.) which you don’t want?

Then do

ls | sed 's/\(.*\)_.*/\1/'

This matches the longest possible string (the first .*) that is followed by an _, and then everything after it, and replaces it with only the first string.

0

Don't parse the output of ls:

$ awk 'BEGIN{for (i=1;i<ARGC;i++) { f=ARGV[i]; sub(/_[^_]*$/,"",f); print f }; delete ARGV}' *
abc_def_g
bbc
zz_b

That would work even if your file names contained newlines or any other characters.

Ed Morton
  • 31,617
0

Using Raku (formerly known as Perl_6)

~$ raku -e 'for dir(test => / .+ \.sql $ /) {.Str.subst(/  _ <-[_]>*? $ /).say};'

Above is a solution coded in Raku, a member of the Perl-family of programming languages.

The solution above requires no call to ls, rather the dir() method in Raku can take a Regex-matcher, to look (for example) for files ending in .sql. Then those files can be examined individually, substituting with nothing (i.e. deleting) the two regex atom pattern _ (underscore), and the bespoke character class <-[_]>*? which frugally-matches non-underscore characters (if any) occurring $ at the end-of-string. File names ending in .sql but containing no underscores will be returned unchanged.

Nota bene, the hope of such an approach--despite being posted on a Linux/Unix SE site--is that this code will run unchanged on WSL (see SO link below).

https://stackoverflow.com/a/71973303/7270649
https://raku.org

jubilatious1
  • 3,195
  • 8
  • 17