Ben Farmer
Ben Farmer

Reputation: 2854

'find' files containing an integer in a specified range (in bash)

You'd think I could find an answer to this already somewhere, but I am struggling to do so. I want to find some log files with names like

myfile_3.log

however I only want to find the ones with numbers in a certain range. I tried things like this:

find <path> -name myfile_{0..67}.log #error: find: paths must precede expression
find <path> -name myfile_[0-67].log #only return 0-7, not 67
find <path> -name myfile_[0,67].log #only returns 0,6,7
find <path> -name myfile_*([0,67]).log # returns only 0,6,7,60,66,67,70,76,77

Any other ideas?

Upvotes: 5

Views: 9344

Answers (5)

oliv
oliv

Reputation: 13249

If you want to match an integer range using regular expression, use the option -regex in the your find command.

For example to match all files from 0 to 67, use this:

find <path> -regextype egrep -regex '.*file([0-5][0-9]|6[0-7])\.txt'

There are 2 parts in the regex:

  • [0-5][0-9] matches the range 0-59
  • 6[0-7] matches the range 60-67

Note the option -regextype egrep to have extended regular expression.
Note also the option -regex matches the whole filename, including path, that's the reason of .* at the beginning of the regex.

Upvotes: 6

Mark Setchell
Mark Setchell

Reputation: 207445

You can do this simply and concisely, but admittedly not very efficiently, with GNU Parallel:

parallel find . -name "*file{}.txt" ::: {0..67}

In case, you are wondering why I say it is not that efficient, it is because it starts 68 parallel instances of find - each looking for a different number in the filename... but that may be ok.

Upvotes: 3

pjh
pjh

Reputation: 8064

One possibility is to build up the range from several ranges that can be matched by glob patterns. For example:

find . -name 'myfile_[0-9].log' -o -name 'myfile_[1-5][0-9].log' -o -name 'myfile_6[0-7].log'

Upvotes: 0

RobC
RobC

Reputation: 24982

The following will find all files named myfile_X.log - whereby the X part is a digit ranging from 0-67.

find <path> -type f | grep -E "/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$"

Explanation:

  • -type f finds files whose type is file.

  • | pipes the filepath(s) to grep for filtering.

  • grep -E "/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$" performs an extended (-E) regexp to find the last part of the path (i.e. the filename) which:

    • begins with myfile_
    • followed with a digit(s) ranging from 0-67.
    • ends with .log

Edit:

Alternatively, as suggested by @ghoti in the comments, you can utilize the -regex option in the find command instead of piping to grep. For example:

find -E <path> -type f -regex ".*/myfile_([0-9]|[0-5][0-9]|6[0-7])\.log$"

Note: The regexp is very similar to the previous grep example shown previously. However, it begins with .*/ to match all parts of the filepath up to and including the final forward slash. For some reason, unknown to me, the .*/ part is not necessary with grep1.


Footnotes:

1If any readers know why the ERE utilized with find's -regex option requires the initial .* and the same ERE with grep does not - then please leave a comment. You'll make me sleep better at night ;)


Upvotes: 1

Poshi
Poshi

Reputation: 5762

You cannot represent a general range with a regular expression, although you can craft a regex for a specific range. Better use find to get files with a number and filter the output with another tool that perform the range checking, like awk.

START=0
END=67
while IFS= read -r -d '' file
do
    N=$(echo "$file" | sed 's/file_\([0-9]\+\).log/\1/')
    if [ "$N" -ge "$START" -a "$N" -le "$END" ]
    then
        echo "$file"
    fi
done < <(find <path> -name "myfile_*.log" -print0)

In that script, you perform a find of all the files that have the desired pattern, then you loop through the found files and sed is used to capture the number in the filename. Finally, you compare that number with your range limits. If the comparisons succeed, the file is printed.

There are many other answers that give you a regex for the specific range in the example, but they are not general. Any of them allows for easy modification of the range involved.

Upvotes: -1

Related Questions