0

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.

adob
  • 3
  • 3

3 Answers3

0

Is this what you're trying to do (untested)?

fullPath='first/second/third'
mapfile -t -d '/' dirs <<<"${fullPath}/"
numDirs=$(( ${#dirs[@]} - 1 ))
path=''
file='x'
for (( dirNr=1; dirNr<=numDirs; dirNr++ )); do
    path="${path}${dirs[$dirNr]}/"
    if [[ -f "${path}${file}" ]]; then
        printf 'Found "%s"\n' "${path}${file}"
    fi
done
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

Now I found another solution using the internal field separator. The / directory is also considered.

path="/d1/d2/d3"
file=x

IFS='/'
unset current_path
for current_dir in $path; do
    current_path="${current_path}${current_dir}/"
    if [ -f "${current_path}${file}" ]; then
        printf '%s\n' "$current_path"
    fi  
done
unset IFS
adob
  • 3
  • 3
0

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
tshiono
  • 21,248
  • 2
  • 14
  • 22
  • Any suggestions how to traverse in the order as described above, i.e. `aa`, `aa/bb`, ...? – adob May 14 '19 at 01:49
  • I've updated my answer according to your requirement. It is a simple (not smart) solution by storing the paths in an array and process them in the reverse order. BR. – tshiono May 14 '19 at 03:00
  • Now I encountered the problem when using absolute paths that `/` is not traversed. In most cases this is not relevant, but for algorithmic completeness it would be nice if this happens too. BR. – adob May 14 '19 at 10:45
  • I've updated my answer accepting your proposal. I'd suggest you to update your original post accordingly. BR. – tshiono May 15 '19 at 04:03