How to pass a regex when finding a directory path in bash?

This is a surprisingly tricky thing to do nicely.

Fundamentally, -d will only test a single argument - even if you could match filenames using a regular expression.

One way would be to flip the problem around, and test directories for a regex match instead of testing the regex match for directories. In other words, loop over all the directories in $HOME using a simple shell glob, and test each against your regex, breaking on a match, finally testing whether the BASH_REMATCH array is non-empty:

#!/bin/bash

for d in "$HOME"/*/; do
  if [[ $d =~ (ana|mini)conda[0-9]? ]]; then
    break;
  fi
done

if ((${#BASH_REMATCH[@]} > 0)); then
    echo "anaconda/miniconda directory is found in your $HOME"
  else
    echo "anaconda/miniconda is not found in your $HOME"
fi

An alternate way would be to use an extended shell glob in place of the regex, and capture any glob matches in an array. Then test if the array is non-empty:

#!/bin/bash

shopt -s extglob nullglob

dirs=( "$HOME"/@(ana|mini)conda?([0-9])/ )

if (( ${#dirs[@]} > 0 )); then
  echo "anaconda/miniconda directory is found in your $HOME"
else
  echo "anaconda/miniconda is not found in your $HOME"
fi

The trailing / ensures that only directories are matched; the nullglob prevents the shell from returning the unmatched string in the case of zero matches.


To make either recursive, set the globstar shell option (shopt -s globstar) and then respectively:-

  • (regex version): for d in "$HOME"/**/; do

  • (extended glob version): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


Indeed, as already mentioned, this is tricky. My approach is the following:

  • use find and its regex capabilities to find the directories in question.
  • let find print an x for each found directory
  • store the xes in a string
  • if the string is non-empty, then one of the directories was found.

Thus:

xString=$(find $HOME -maxdepth 1 \
                     -type d \
                     -regextype egrep \
                     -regex "$HOME/(ana|mini)conda[0-9]?" \
                     -printf 'x');
if [ -n "$xString" ]; then
    echo "found one of the directories";
else
    echo "no match.";
fi

Explanation:

  • find $HOME -maxdepth 1 finds everything below $HOME but restricts the search to one level (that is: it doesn't recurse into subdirectories).
  • -type d restricts the search to only directories
  • -regextype egrep tells find what type of regular expression we deal with. This is needed because things like [0-9]? and (…|…) are somewhat special and find doesn't recognize them by default.
  • -regex "$HOME/(ana|mini)conda[0-9]?" is the actual regular expression we want to lookout for
  • -printf 'x' just prints an x for every thing that satisfies the previous conditions.