I'm trying to write a bash line that will look at all the subdirectories in my current folder and tell me if any do NOT contain a ".git". Pseudo:

for subdir in currentdir
  if .git does not exist
    print subdir

Here's the one-liner I'm trying, which isn't working and seems to print all the subdirectories:

find . -maxdepth 1 -type d -execdir $SHELL -c '[ ! -d ".git" ] && echo "not git repo:" {}' $SHELL -c '{}' ';'

I've found some solutions that let me print all subdirectories that HAVE a .git, like this one -- I'm trying to do the opposite.

What am I doing wrong? Is this possible?


First, the -execdir command looks wonky (2x $SHELL -c?):

$SHELL -c '[ ! -d ".git" ] && echo "not git repo:" {}' $SHELL -c '{}' ';'

Also, -execdir runs the command in the directory containing the matched entity, (so the parent directory of the directory that it is checking right now), which is . for all subdirectories. The test is run in the wrong directory. And:

  • You shouldn't reuse {} in exec/-execdir commands.
  • There's no reason to use $SHELL. That's the user's login shell, and it doesn't have any special significance for usage in scripts and such. Just use sh, or bash or ksh if those are your shells of choice, directly.

This might work:

find . -maxdepth 1 -type d -exec sh -c '! [ -d "$1/.git" ] && echo "not git repo: $1"' _ {} \;

The -exec command checks for the existence of .git in the argument passed in, which is each subdirectory in turn. You might also want to use -mindepth 1 to exclude the current directory:

find . -maxdepth 1 -mindepth 1 -type d -exec sh -c '! [ -d "$1/.git" ] && echo "not git repo: $1"' _ {} \;

Or, just using bash, enabling dotglob to match hidden directories:

(shopt -s dotglob; for d in ./*/; do [[ -d $d/.git ]] || echo "not git repo: $d"; done)
  • Thank you! I found the $SHELL bit (somewhere...) as a solution to "you can't do test using exec" but as these answers prove, that appears to have been incorrect. Can you explain why I shouldn't reuse {} in exec/execdir? – Victoria Drake Jun 18 at 11:52
  • 1
    It may not work correctly depending on the version of find: stackoverflow.com/a/12965441/2072269 – muru Jun 18 at 11:56

muru’s answer explains what’s wrong with your approach. The answers to the question you referred to suggest a simpler approach with no shell involved:

find . -maxdepth 1 -type d -exec test '!' -e '{}/.git' ';' -printf "not git repo: %p\n"

(assuming your find supports -printf), or

find . -maxdepth 1 -type d -exec test '!' -e '{}/.git' ';' -print

This negates the test run using -exec test to get the desired result.

  • Thank you! The negated test is a much simpler approach as well. – Victoria Drake Jun 18 at 11:54

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.