Search code examples
bashshellfinddirectory-traversalpath-traversal

How to traverse directories a path consists of in BASH


I'm about to program a file parser which is operating in a directory tree structure. Once I find a specific leaf directory I want to go through all directories which the path consists of and do some operations within them.

Let's say the path is: /d1/d2/d3. Now I want to check whether or not a file x is present in /d1, /d1/d2 and /d1/d2/d3 respectively and in that order.

Of course, one could do something like this:

fields=`find $base_dir -name "leaf_directory" | grep  -o "/" | wc -l`

[[ $fields > 0 ]] || exit 1

for (( i=1; i <= $fields + 1; i++ )) do
    current_dir="`find $base_dir -name "leaf_directory" | cut -d "/" -f $i`"
    source_path="$source_path$current_dir/"

    if [ -f $source_path$file ]; then
        # do sth.
    fi  
done

But is there any more elegant solution for this?

Thank you.


Solution

  • Please try the following:

    path="aa/bb/cc"
    file="x"
    
    while true; do
        if [[ -f "$path/$file" ]]; then
            echo "Found: $path/$file"
        fi
        if [[ $path =~ ^/?[^/]+$ ]]; then
            break
        fi
        path="${path%/*}"
    done
    

    It comes down to the problem how to generate upper directories from the given path. My code above will work for the both cases of absolute path and relative path. In order to accept the path which starts with . or .., a small modification in the regexp will be needed.

    [EDIT]

    If you want to process in the order as aa, aa/bb, .., please try the following:

    path="aa/bb/cc"
    file="x"
    
    while true; do
        array+=("$path")
        if [[ $path =~ ^/?[^/]+$ ]]; then
            break
        fi
        path="${path%/*}"
    done
    for (( i=${#array[@]}-1; i>=0; i-- )); do
        p="${array[$i]}"
        if [[ -f "$p/$file" ]]; then
            echo "Found: $p/$file"
        fi
    done
    

    [EDIT]

    If you want to include the root directory / in the search path when an absolute path is specified, please try:

    path="/aa/bb/cc"
    file="x"
    
    while true; do
        array+=("$path")
        if [[ $path =~ ^(/|[^/]+)$ ]]; then
            break
        elif [[ $path =~ ^/[^/]+$ ]]; then
            path="/"
        else
            path="${path%/*}"
        fi
    done
    for (( i=${#array[@]}-1; i>=0; i-- )); do
        p="${array[$i]}"
        if [[ -f "$p/$file" ]]; then
            echo "Found: $p/$file"
        fi
    done