#!/bin/bash # # dont-go-there.sh # # From "Don't go there: Keeping the Unix 'find' command out of your CVS # and Subversion directories." # # http://forwardlateral.com/blog/2006/02/27/dont-go-there/ # Copyright 2006 by Michael Birk, mbirk@forwardlateral.com # ########################################################################## # # This is a wrapper around the standard external "find" utility, which # automatically prunes Subversion and CVS metadata directories. # # It works by modifying the find expression to include an additional # constraint. This technique is complicated by the non-standard # command-line syntax used by find. # # Installation is simple -- just source this file from your .bashrc or # .bash_profile script. For example: # # source /path/to/dont-go-there.sh # # The script hides the external find command with a shell function of # the same name, making the functionality transparent. # # As a special escape mechanism, if the (very) first argument to find is # -debug, then it prints (to stderr) the modified find command line. For # example: # # > find -debug -name \*.sh # find . ! ( ( -name .svn -o -name CVS ) -prune ) -name *.sh # ###################################################################### # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################################################### # # Changes: # 1.0 -- 2006 Feb 27 -- Initial Release # function find () { # If the very first argument (even before -H) is -debug, then print out # the complete find command before running it. local debug=0 if [[ x$1 == x-debug ]]; then debug=1 shift fi # Any initial -H, -L, or -P arguments must come at the beginning. We # will store the ones that we find in the "options" array. local -a section1 while (( $# > 0 )); do local arg=$1 # Push global options onto the "section1" array. if find_matches "$arg" "${find_global_options[@]}" ; then section1[${#section1[@]}]=$arg; shift # Otherwise move on to the next section. else break fi done # Next come the path names. These are identified by the fact that they do # not start with a dash. # # Note that here we set a default value of the current directory. So that # this can be overwritten, we use a separate path_index to keep track of # position. local -a section2=(.) local section2_index=0 while (( $# > 0 )); do local arg=$1 # Push paths onto the "section2" array. if [[ x${arg:0:1} != x- ]]; then section2[$((section2_index++))]=$arg; shift # Otherwise move on to the next section. else break fi done # Finally we have the expression, which starts with various options, # each beginning with the dash. However, there are several "special" # options that must come at the beginning, before our inserted condition # for pruning directories. We simply append them to the "paths" array as # well. while (( $# > 0 )); do local arg=$1 local param=$2 # If this is a special option (with or without parameters), then move # it to the paths array. if find_matches "$arg" "${find_special_options[@]}"; then section2[$((section2_index++))]=$arg; shift elif find_matches "$arg" "${find_special_param_options[@]}"; then section2[$((section2_index++))]=$arg ;shift section2[$((section2_index++))]=$param ;shift # Otherwise move on to the next section. else break fi done # Anything left is the expression, which goes at the end, after the # inserted $find_options. find_debug $debug find "${section1[@]}" "${section2[@]}" $find_options "$@" } # Simple helper function to optionally print the command line before # executing it. Used when the -debug option is given. function find_debug () { local debug=$1; shift if (( $debug )); then echo "$@" 1>&2; fi command "$@" } # Returns 0 if the first argument matches any of the remaining arguments, # 1 otherwise. function find_matches () { local value1=$1; shift local value2 for value2 in "$@"; do if [[ x$value1 == x$value2 ]]; then return 0; fi done return 1; } # New options that are inserted after any "special" options (such as -depth), # but before the "normal" expression (e.g. -name). find_options="! ( ( -name .svn -o -name CVS ) -prune )" # Global options that must go up-front, before any paths (but after our # synthetic -debug option). find_global_options=( -H -L -P ) # "Special" options that should go at the front of the expression. If one # of these is not at the front, the external find command issues a warning. find_special_options=( -daystart -d -depth -follow -help --help -ignore_readdir_race -mount -noignore_readdir_race -noleaf -version --version -xdev ) # These are "special" options that also take a single parameter, which # immediately follows it. find_special_param_options=( -maxdepth -mindepth -regextype )