2

I have some files with numeric names: 2341a.po, 4567211someword.po, 0012.po, etc. I would like to find a set of files based on the numeric range. such as [126 - 363].

Normally I use regular expression with find. Any numeric range [N, M] can be composed with two regular expressions larger than N and less than M.

The larger than N:

If N = vxyz, then I first do a match for all value > V000, (V=v+1) with [V-9]\d{3,}

Then match vX00, X=x+1, with v[X-9]\d\d

Then match vxY0, Y=y+1, with vx[Y-9]\d

And last vxy[z-9]

Example:

To match number>=234, I use:

`^(0*([3-9]\d{2,}|2[4-9]\d|23[4-9]))`

The smaller than M:

Based on similar logic we will have:

^(0*(vxy[0-z]|vx[0-Y]\d|v[0-X]\d\d|[1-V]\d\d|\d{1,3}))[^0-9]

With Y=y-1,X=x-1,V=v-1

For example, the follow command will find any file between [253, 326]:

find . -maxdepth 1 -type f -regextype posix-extended -iregex '^\./0*([3-9][0-9]{2,}|2[6-9][0-9]{2,}|25[3-9]).*' -iregex '^\./0*(32[0-6]|3[0-1][0-9]|[0-2][0-9][0-9]|[0-9]{1,2})[^0-9].*'

However, this method is too annoying to handle long numbers. Is there any better and easier way to do this?

2

With zsh:

setopt extendedglob # best in ~/.zshrc
ls -ld -- <126-253>(*.po~[0-9]*)

Recursively, including in hidden dirs and only regular files:

ls -ld -- **/<126-253>(*.po~[0-9]*)(D.)

use zargs if you run into the arg list too long error.

1

You could generate a list of files with the numbers that you want to find and put them on the argument list for find using xargs. For example, using bash, the equivalent to using -maxdepth 1 as in the question would be:

echo {253..326} | xargs sh -c 'find "$@" -type f -maxdepth 0' sh

Note that you could use the -I option to xargs (ie xargs -I{} find {} -type f), but GNU xargs forces -L 1 with this option, meaning that a separate find process is started for each argument. Using sh gets around this issue.

For an arbitrary depth you can do:

printf -- '-o -name %d ' {254..326} |
  xargs -n 3000 sh -c 'find -type f \( -name 253 "$@" \)' sh

The -n argument (maximum number of arguments added per command) should be chosen so it will limit the size of the argument list xargs constructs. If it is too large the limit may be reached due to the overall size of the argument list rather than the number of arguments. It should be a multiple of 3 so that you are not left with a trailing -o or -name in the list.

  • Great! This is a very smart solution! However, if the list is very long and there are lots of files, it needs to repeat the find too many times. Thus this method seems take extremely longer time than regex method. Any comment on this? – Wang Dec 30 '14 at 2:25
  • @Wang, it shouldn't be starting too many different find processes. If it is, that sounds like an issue with the version of xargs you are using. I never tested this on a large number of files though, it could be generally quite slow even with one process. A lot depends on the find implementation which probably isn't optimised for this kind of thing. What was the command you used? Did you add any globs to the search string? – Graeme Dec 30 '14 at 10:55
0

A brute-force extension on Graeme's answer:

find . -type f -regextype posix-awk -regex ".*/0*($(seq -s'|' 254 456)).*" 

You can use \| instead of | and \(\) instead of () if you need POSIX (but then, seq is not POSIX, is it?).

$ find . -maxdepth 2 -type f -regextype posix-awk -regex ".*/0*($(seq -s'|' 254 456)).*"    
./.fontconfig/3047814df9a2f067bd2d96a2b9c36e5a-le32d4.cache-3
./.fontconfig/3830d5c3ddfd5cd38a049b759396e72e-le32d4.cache-3
./.fontconfig/385c0604a188198f04d133e54aba7fe7-le32d4.cache-3
./Documents/374620-63301.pdf
./4567211someword.po

Hmm. Looks like we should add a non-digit character to mark the end of the number. Perhaps ".*/0*($(seq -s'|' 254 456))[^0-9].*"?

  • 2
    The problem here is that it won't work if the argument list is too long. The reason for preferring xargs in a situation like this is that it will just start another find process once the current argument list is full. – Graeme Dec 26 '14 at 11:17
0
find | perl -ne 'print if(m!^\./(\d+)! and $1 > 126 and $1 <363)'

...possibly adding some of the good ideas presented in the other answers.

Regex may need some tuning (eg: ^\./(\d+)\w*.po$)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.