Home > Net >  How to find a list of files that are of specific extension but do not contain certain characters in
How to find a list of files that are of specific extension but do not contain certain characters in

Time:05-03

I have a folder with files that have extensions, such as .txt, .sh and .out.

However, I want a list of files that have only .txt extension, with the file names not containing certain characters.

For example, the .txt files are named L-003_45.txt and so on all up to L-003_70.txt. Some files have a change in the L-003 part to lets say L-004, creating duplicates of lets say file 45, so basically both L-003_45.txt and L-004_45.txt exist. So I want to get a list of text files that don't have 45 in their name.

How would I do that?

I tried with find and ls and succeeded but I would like to know how to do a for loop instead.

I tried: for FILE in *.txt; do ls -I '*45.txt'; done but it failed.

Would be grateful for the help!

CodePudding user response:

Or you use Bash's extendedglobing

#!/usr/bin/env bash

# Enables extended globing
shopt -s extglob

# Prevents iterating patterns if no match found
shopt -s nullglob

# Iterates files not having 45 or 57 before .txt
for file in !(*@(45|57)).txt; do
  printf '%s\n' "$file"
done

CodePudding user response:

I would advise you to use the find command to find all files with the required extensions, and later filter out the ones with the "strange" characters, e.g. for finding the file extensions:

find ./ -name "*.txt" -o -name "*.sh" -o name "*.out"

... and now, for not showing the ones with "45" in the name, you can do:

find ./ -name "*.txt" -o -name "*.sh" -o name "*.out" | grep -v "45"

... and if you don't want "45" nor "56", you can do:

find ./ -name "*.txt" -o -name "*.sh" -o name "*.out" | grep -v "45" | grep -v "56"

Explanation:

  • -o stands for OR
  • grep -v stands for "--invert-match" (not showing those results)

CodePudding user response:

Setup:

$ touch L-004_23.txt L-003_45.txt L-004_45.txt L-003_70.txt

$ ls -1 L*txt
L-003_45.txt
L-003_70.txt
L-004_23.txt
L-004_45.txt

One idea using ! to negate a criteria:

$ find . -name "*.txt" ! -name "*_45.txt"
./L-003_70.txt
./L-004_23.txt

Feeding the find results to a while loop, eg:

while read -r file
do
    echo "file: ${file}"
done < <(find . -name "*.txt" ! -name "*_45.txt")

This generates:

file: ./L-003_70.txt
file: ./L-004_23.txt

CodePudding user response:

The proposed solution with extglob is a very good one. In case you need to exclude more than one pattern you can also test and continue. Example to exclude all *45.txt and *57.txt:

declare -a excludes=("45" "57")
for f in *.txt; do
  for e in "${excludes[@]}"; do
    [[ "$f" == *"$e.txt" ]] && continue 2
  done
  printf '%s\n' "$f"
done
  • Related