Hello, I have a use case where I have to find files by multiple criteria like directory suffix and a filename.
dir_A/B_file
dir_A/A_file
dir_B/B_file
where I need to find only directories with _A and files B_.
Of course, I can find all B_ prefixed files and use grep to filter directories, but I'm wondering if there is a way to do that in one step.
I tried regexp look around, but got an error error: look-around, including look-ahead and look-behind, is not supported
For some reason, fd | grep '_A.*B_' works as expected while fd '_A.*B_' returns an empty result. I'm wondering if that's because of some optimization.
Is there any syntactic way to chain search criteria?
a less than ideal workaround would be something like: fd _A | grep B_
in my experience, that won't work.
bash-5.0$ cd /tmp
bash-5.0$ mkdir test
bash-5.0$ cd test
bash-5.0$ mkdir dir_{A,B}
bash-5.0$ ls
dir_A dir_B
bash-5.0$ touch dir_{A,B}/{A,B}_file
bash-5.0$ tree
.
โโโ dir_A
โย ย โโโ A_file
โย ย โโโ B_file
โโโ dir_B
โโโ A_file
โโโ B_file
2 directories, 4 files
bash-5.0$ fd _A | grep B_
# nothing
bash-5.0$ fd B_ | grep _A
dir_A/B_file
bash-5.0$
my understanding is that fd has some optimization that stops traversing into subdirectories when it finds the match. It does not match path, it only looks for leaf (filename or directory) names
I think you want -p/--full-path
As @tavianator said, you need to use --full-path to match on the whole file path (instead of just the basename). So this should work:
fd -p '_A.*B_'
If you want directories that end with _A, you should probably use _A/.*B_. In addition, if you want to make sure that there is no / after B_ (otherwise it would be a directory including B_), you could use _A/.*B_[^/]*$.
Agreed, all of this is not very pretty and it's definitely one of those scenarios where find (or maybe fselect) might actually be more suited to the task. There is currently no way to use two patterns. Other filters are usually AND-ed, though (using a pattern, --type and --extension, for example).
my understanding is that
fdhas some optimization that stops traversing into subdirectories when it finds the match.
There is no such behavior. We could potentially miss search results otherwise. If you search for foo (with --full-path), you will find bar/foo, but also bar/foo/1 and bar/foo/2.
Thanks for detailed explanation. I think this knowledge deserve place is example section of the man page.
Most helpful comment
As @tavianator said, you need to use
--full-pathto match on the whole file path (instead of just the basename). So this should work:If you want directories that end with
_A, you should probably use_A/.*B_. In addition, if you want to make sure that there is no/afterB_(otherwise it would be a directory includingB_), you could use_A/.*B_[^/]*$.Agreed, all of this is not very pretty and it's definitely one of those scenarios where
find(or maybefselect) might actually be more suited to the task. There is currently no way to use two patterns. Other filters are usually AND-ed, though (using a pattern,--typeand--extension, for example).