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 anx
for each found directory - store the
x
es 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 onlyd
irectories-regextype egrep
tellsfind
what type of regular expression we deal with. This is needed because things like[0-9]?
and(…|…)
are somewhat special andfind
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 anx
for every thing that satisfies the previous conditions.
You can loop over a list of directory names you want to test and act on it if one of them exists:
a=0
for i in {ana,mini}conda{,2}; do
if [ -d "$i" ]; then
unset a
break
fi
done
echo "anaconda/miniconda directory is ${a+not }found in your $HOME"
This solution obviously doesn’t allow for the full regex power, but shell globbing and brace expansion is equal at least in the case you showed. The loop exits as soon as one directory exists and unsets the previously set variable a
. In the subsequent echo
line, the parameter expansion ${a+not }
expands to nothing if a
is set (= no dir found) and “not ” else.