0

I have a find command and I would like to issue it from an arbitrary directory with a $directory variable in place of the dot . for the current working directory.

What works is this

  • Example 1
find $directory -maxdepth 1 -type d -name "test*"

That gives me some results.

Also this works

  • Example 2
find . -maxdepth 1 -type d -regextype posix-extended -regex "^./[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$"

However, with a $directory it doesn't work. Mind that I removed the ./ at the beginning of the regex.

  • Example 3
find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$"

Why is that so?

Thanks

1 Answer 1

5

-regex (not standard), like the standard -path matches on the full path of the files. Those paths start with the contents of $directory followed by the dirs and files found by find. So if $directory is /some/dir, -regex will match the regex against /some/dir/file-discovered-by-find, and will never match here as the first character is a /, not an alnum.

In the first case, your regexp started with ^./ (should have been ^\./ as . is a regexp operator, or just \./ as ^ is implied in GNU find's -regex) as $directory there was ..

You'd need to do something similar with $directory, bearing in mind that all regexp operators in $directory (^$*()+[]?.\...) would need to be escaped. But here you might as well do:

find "$directory" -maxdepth 1 -type d -regextype posix-extended \
  -regex '.*/[[:alnum:]]+([-_][[:alnum:]]+)*-[[:digit:]]{1,3}(\.[[:digit:]]{1,3}){0,3}'

(I've also replaced . with \. as I suspect you want to match a literal . and not just any character, and removed the ? which was not necessary here).

That is, have your pattern (which here is guaranteed not to encompass a /) match after any / (and up to the end of the file path as $ is also implied) rather than after something that matches $directory.

With zsh -o extendglob, you can do:

set -o extendedglob
w=[0-9a-zA-Z] d=[0-9]
print -rC1 -- $directory/$~w##([-_]$~w##)#-$~d(#c1,3)(.$~d(#c1,3))(#c0,3)(#q/)

Which has several advantages over the GNU find one:

  • would work even on non-GNU systems (removes the dependency to GNU find)
  • works even if $directory starts with -
  • gives you a sorted list
  • in zsh, what [a-zA-Z0-9] matches is not random and doesn't vary with the locales like in find and matches the characters I suppose you want matched and only them. In find, you'd need [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789] to get the equivalent.
  • it works even if $directory is a symlink to a directory (you can add the -H option to find for that though).
  • it works even if the file paths (in this case the contents of $directory) contains sequence of bytes that don't form valid characters in the current locale (find's -regex's .* could not match on those as . only matches valid characters).
  • instead of printing that list, you can use it directly (as arguments to a command, to loop over it, etc.)
  • you can also use things like (<0-255>~????*) to match decimal numbers from 0 to 255 made of no more than 3 digits or (<0-255>~0?*) for those numbers without leading 0, zsh globs being one of the very few pattern matching APIs that support matching ranges of numbers.
4
  • Thanks for your solution ! I learned alot and will try your solution! But I also changed my regex like this "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$" which now works with the find command but is weird because why would I specify the directory as second argument just to only be able to get results by prepending the $directory a second time to the regex? However. You gave the explanation why this is so. Thanks
    – von spotz
    May 29, 2021 at 7:37
  • 1
    @vonspotz, but like I said, you'd need to escape regexp operators in $directory first. For instance, if $directory is [1] dir, that won't work. Also as I said, the ^, $ are superflous, so is ?. . should be escaped if you want it to match a literal . and [a-z] would match a lot more characters than you might thing it does. May 29, 2021 at 7:44
  • Yes I saw my mistake that I didn't escape the dot. But btw: What if I don't use zsh? And what's the default for set -o extendedglob so I can set it back to the default after the search?
    – von spotz
    May 29, 2021 at 7:47
  • extendedglob is off by default in zsh, but you likely want it on all the time. It's off by default for backward compatibility so that ~, ^ and # are not treated specially, but those characters should in general be quoted if you want them to be literal as they have other special meanings in zsh and most shells. May 29, 2021 at 7:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .