my favorite shell scripts: fast find


Here’s another incredibly useful bit of shell. Everyone knows how useful the find command is, but probably doesn’t use it as much as they should, because the syntax is a little odd and picky. Here’s my attempt to simplify it for common interactive uses.

It’s based on three observations:

  1. The most common thing I do with find is find ‑name \*foo\*.
  2. When I pass directory names to find, I almost always tab-complete them, and when I do that, bash inserts a slash.
  3. I often add stuff to the end of a previous command, like ‑print0, but sometimes I want to add a directory also.

So, this little wrapper around find looks at each arg and does one of three things: If it has a slash in it, it’s a directory and it goes at the front of the final command line. If it starts with a dash, it’s flag, and it goes at the end. Everything else is a substring to search for in a filename.

That’s it. The only tricky feature is when you want to pass an argument to a flag, and not have it interpreted as a substring match. To handle that, flags are deliberately double-expanded, so you can escape the space like ff foo ‑mtime\ 0.

ff () {
  local IFS=$' \t\n'
  local -a args
  local -a names
  local -a paths
  local dasho=''
  local d
  for d; do
    case "$d" in
      # let this get expanded
      -*)  args+=($d) ;;
      # quote this
      */*) paths+=("$d") ;;
      # process this into a partial find expression
      *)   names+=($dasho -name "*$d*"); dasho='-o' ;;
    esac
  done
  find "${paths[@]}" \( "${names[@]}" \) "${args[@]}"
}