3

Is there any equivalent command, or options for, GNU find that will search up the directory tree? I'd like to look backwards through the tree for files with a given name. For example, say I'm in /usr/local/share/bin and I want to search look for a file called foo. Ideally I'd like the command to look for the file in the following order:

  1. /usr/local/share/bin/foo
  2. /usr/local/share/foo
  3. /usr/local/foo
  4. /usr/foo
  5. /foo

I know that I can write something like this as a shell function, but I was hoping there would be a command as rich as gnu find that I could leverage.

migrated from serverfault.com May 2 '14 at 10:56

This question came from our site for system and network administrators.

  • 2
    Have you tried find with the -depth option? -depth Process each directory's contents before the directory itself.. – Zoredache Apr 29 '14 at 5:54
  • As far as I can tell, the -depth switch just changes the find's mode of operation in terms of which directories it looks at first. It doesn't appear to make find look upwards through the directory hierarchy. – Bryan Kyle Apr 29 '14 at 16:25
  • What exactly do you mean by upwards? When doing a tree search you either do depth first, or breadth first. A depth first search should return results like you describe above. – Zoredache Apr 29 '14 at 16:36
  • find looks downwards through the directory structure, towards the leaves of the file system. I want to look upwards, meaning towards the root of the file system. See the list in my original question. Notice that it starts with a longer path (deeper in the file system) and moves towards towards short paths (shallower in the file system). – Bryan Kyle Apr 30 '14 at 15:38
  • And how do you think a command could actually acomplish that? The is no magical way for a command to know the deepest folder. Are you going to pass the starting folder on the command line, or? – Zoredache Apr 30 '14 at 15:48
1

The following uses find and would still search from the root directory down to the current directory, but would only look inside the directories on that single path:

find / -exec bash -c '[[ $PWD != "$1"* ]]' bash {} \; -prune -name foo -print

The first part of the command, up to the -prune, will determine whether the current pathname being examined is located in the path of $PWD. If it's not on the way to the current directory, it is pruned from the search path.

The comparison is carried out by a very short bash script that simply tests whether the current directory, $PWD, matches the start of the current pathname.

The bit after -prune simply tries to match foo against the filename that is being examined.

Example: Trying to find something called bin somewhere in the directory structure above where I'm currently at.

$ pwd
/home/kk/local/build/ast/build/src/cmd/ksh93/sh
$ find / -exec bash -c '[[ $PWD != "$1"* ]]' bash {} \; -prune -name bin -print
/bin
/home/kk/local/bin
/home/kk/local/build/ast/bin

On systems without bash, or where there is a faster-to-start sh shell:

find / -exec sh -c 'case $PWD in ("$1"*) exit 1; esac' sh {} \; -prune -name bin -print
  • This post is excellent, an eye-opener, but I can't seem to find a web resource covering these kind of constructs. Any pointers? – bu5hman Jun 7 at 18:14
  • @bu5hman I have never read a tutorial on how to use find. I have, however, used it a lot and read its manual. I'm sorry, but I don't have a magic web site for you. Oh, yes I have... Continue answering questions here, and read other people's answers :-) – Kusalananda Jun 7 at 18:17
  • would I be right in understanding that an -exec can be inserted after any option to process the output at that stage of the search, to control what is passed to the next option? – bu5hman Jun 7 at 18:24
  • @bu5hman Yes, exactly. I did write a sort of "canonical answer" on the use of find ... -exec .... See Understanding the -exec option of `find` – Kusalananda Jun 7 at 19:19
  • we are not worthy.... – bu5hman Jun 7 at 19:21
0
find_backwards () {
  test $# -ne 1 && return 2
  if [ -z "$find_backwards_recurse" ]; then
    pushd . &>/dev/null
    find_backwards_recurse=yes
  fi
  if [ -f "$1" ]; then
    echo "${PWD}/${1}"
    popd &>/dev/null
    find_backwards_recurse=
    return 0
  else
    if [ / != "$PWD" ]; then
      cd ..
      find_backwards "$1"
    else
      popd &>/dev/null
      find_backwards_recurse=
      return 1
    fi
  fi
}

start cmd:> pwd
/home/hl/tmp/tmp

start cmd:> find_backwards foo
/home/hl/tmp/foo
0

I don't know any command who would do that.

One possible script implementation.
rfind:

#!/bin/bash
d=$1
[[ -f  "$d/$2" ]] && echo  "$d/$2" 
until  [[ ${d%%*/*} ]] || [[ -z  "$d"  ]]
do
   d=${d%/*}
   [[ -f  "$d/$2" ]] && echo  "$d/$2" 
done  

Usage rfind path filename

0

Walking up the directory path

f=/home/bu5hman/test; file=${f##*/}; base=${f%/*}
while [ ! -z $base ]; do
    find $base -maxdepth 1 -name $file
    base=${base%/*}
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy