Niel de Wet
Niel de Wet

Reputation: 8416

Find file by name up the directory tree, using bash

Using bash, how can I find a file with a specific name somewhere up the directory tree from the pwd?

To be more clear. I want to find a file that sits in my working directory's root, but I don't know where the root is, and my pwd might be anywhere below the root.

Upvotes: 18

Views: 5640

Answers (4)

Ian Carter
Ian Carter

Reputation: 2166

similar to the answer of @kev but as a function attempting to locate a (sub-)file upwards toward the root folder (prefering string-manipulation over function calls like dirname tr etc)

# @usage: `dir_containing [--stop=<dir>] <sub-path-to-find>`
# @params: 1) (sub-)filename to find, 2) starting-dir (defaults to PWD)
# @options: --stop=<dir> directory where to stop searching (default: <root>)
# @return: directory-path (where the (sub-)filename was found),
#          otherwise empty string (along with a non-zero errorcode)
# @throws: #400 if called without parameters, #404 if not found
# @alias:  upfind, find_upward
dir_containing() {
    local stop=""; [[ "$1" = --stop=* ]] && { stop="$( realpath "${1/--stop=/}" )"; shift; }
    [ "$1" ] || return 400
    local d="$( realpath "${2:-"$PWD"}" )"
    while [ "$d" ] && [ "$d" != "$stop" ] && [ ! -e "$d/$1" ]; do d="${d%/*}"; done
    [ -e "$d/$1" ] && echo "${d:-/}" || return 404
}

Upvotes: 0

kev
kev

Reputation: 161954

Find file.txt up to root

x=`pwd`
while [ "$x" != "/" ] ; do
    x=`dirname "$x"`
    find "$x" -maxdepth 1 -name file.txt
done

Upvotes: 19

MiloDC
MiloDC

Reputation: 2415

local DIR=$(pwd)
while [ ! -z "$DIR" ] && [ ! -f "$DIR/myFile.txt" ]; do
    DIR="${DIR%\/*}"
done
echo $DIR/myFile.txt

Upvotes: 10

Codex24
Codex24

Reputation: 323

I have the following function defined in my ~/.bashrc:

dnif () { 
    # Recursively list a file from PWD up the directory tree to root
    [[ -n $1 ]] || { echo "dnif [ls-opts] name"; return 1; }
    local THERE=$PWD RC=2
    while [[ $THERE != / ]]
        do [[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} $THERE/${2:-$1}; RC=0; }
            THERE=$(dirname $THERE)
        done
    [[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} /${2:-$1}; RC=0; }
    return $RC
}

which will search for a name you provide as a parameter in each directory upwards from the current to the root, and if found, list it with 'ls' and the optional ls -options that you provide. Example output:

me@host:~/dev/example
$ dnif; echo $?
dnif [ls-opts] name
1
me@host:~/dev/example
$ dnif -alp nonesuch; echo $?
2
me@host:~/dev/example
$ dnif -alp .bashrc; echo $?
-rw-r--r-- 1 me mine 3486 Apr  3  2012 /home/me/.bashrc
0
me@host:~/dev/example
$ dnif -d .
/home/me/dev/example/.
/home/me/dev/.
/home/me/.
/home/.
/.

Please note:

  • "dnif" is "find" backwards.
  • The function is a finite loop (not recursive), creates no subshells, and uses Bash built-ins as much as possible for speed.
  • All hits at each ascending directory level are listed.
  • The ls -opts are optional, but must precede the required search argument.
  • The search argument may be a file or directory.
  • If the search argument is a directory, include the ls -opt '-d' to restrict the results to directory names rather than contents.
  • The function returns exit code
    • 0 if there is at least one hit,
    • 1 if no parameters are provided for help, and
    • 2 if nothing is found.

Upvotes: 4

Related Questions