tags:

views:

26

answers:

1

I am trying to look for a certain file in multiple folders. When I hit the file, I want to stop going to sub-directory. For example:

/foo/.target
/bar/buz/.target
/foo/bar/.target

I want only the first two:

/foo/.target
/bar/buz/.target

A: 

Your requirements are not completely clear. I understand them as: look for “wanted” files inside a directory tree; if a directory directly contains at least one match, then just print them, otherwise recurse into that directory.

I can't think of a pure find solution. You could write an awk or perl script to parse the output of find.

Here's a shell script that I think does what you're looking for. Warning: I've only minimally tested it.

#!/bin/sh

## Return 0 if $1 is a matching file, 1 otherwise.
## Note that $1 is the full path to the file.
wanted () {
  case ${1##*/} in
    .target) true;;
  esac
}

## Recurse into the directory $1. Print all wanted files in this directory.
## If there is no wanted file, recurse into each subdirectory in turn.
traverse () {
  found=0
  for x in "$1"/.* "$1"/*; do
    if [ "$x" = "$1/." ] || [ "$x" = "$1/.." ]; then
      continue # skip '.' and '..' entries
    fi
    if ! [ -e "$x" ]; then
      continue # skip spurious '.*', '*' from non-matching patterns
    fi
    if wanted "$x"; then
      printf '%s\n' "$x"
      found=$(($found+1))
    fi
  done
  if [ $found -eq 0 ]; then # no match here, so recurse
    for x in "$1"/.*/ "$1"/*/; do
      x=${x%/}
      if [ "$x" = "$1/." ] || [ "$x" = "$1/.." ]; then
        continue
      fi
      if [ -d "$x" ]; then # only actual subdirs, not symlinks or '.*' or '*'
        found_stack=$found:$found_stack # no lexical scoping in sh
        traverse "${x%/}"
        found=${found_stack%%:*}
        found_stack=${found_stack#*:}
      fi
    done
  fi
}

found_stack=:
for x; do
  if wanted "$x"; then
    printf '%s\n' "$x"
  else
    traverse "$x"
  fi
done
Gilles