8

I'm trying to write a function that will traverse the file directory and give me the value of the deepest directory. I've written the function and it seems like it is going to each directory, but my counter doesn't seem to work at all.

dir_depth(){ 

local olddir=$PWD
local dir
local counter=0
cd "$1"


for dir in *
do
  if [ -d "$dir" ]
  then
    dir_depth "$1/$dir"
    echo "$dir"
    counter=$(( $counter + 1 ))
  fi 
done
cd "$olddir"
}

What I want it to do is feed the function a directory, say /home, and it'll go down each subdirectory within and find the deepest value. I'm trying to learn recursion better, but I'm not sure what I'm doing wrong.

6 Answers 6

9

Obviously find should be used for this

find . -type d -exec bash -c 'echo $(tr -cd / <<< "$1"|wc -c):$1' -- {} \;  | sort -n | tail -n 1 | awk -F: '{print $1, $2}'

At the end I use awk to just print the output, but if that were the output you wanted it would be better just to echo it that way to begin with.

Not that it helps learn about recursion, of course.

Sign up to request clarification or add additional context in comments.

3 Comments

using 'find' -- not necessarily the obvious winner -- it can take a really, really long time to run in some cases; and, in some cases, you've already a ready made list of files available for processing. Using locate (and keeping the rest the same) runs in a few seconds & returns the same result as find which takes several minutes on my laptop (both assume running from . = $PWD); e.g.: locate -0 -r $PWD | xargs -0 bash -c 'echo $(tr -cd / <<< "$1" | wc -c):$1' | sort -n | tail -n 1 | etc...
find is able to display each file's depth on its own, as shown in @kipkoan's answer.
@pedroapero: And when -printf and its format codes are standardized that solution will work for everyone.
4

Just a few small changes to your script. I've added several explanatory comments:

dir_depth(){

    # don't need olddir and counter needs to be "global"
    local dir
    cd -- "$1"    # the -- protects against dirnames that start with -

    # do this out here because we're counting depth not visits
    ((counter++))

    for dir in *
    do
      if [ -d "$dir" ]
      then
        # we want to descend from where we are rather than where we started from
        dir_depth "$dir"
      fi
    done
    if ((counter > max))
    then
        max=$counter      # these are what we're after
        maxdir=$PWD
    fi
    ((counter--))    # decrement and test to see if we're back where we started
    if (( counter == 0 ))
    then
        echo $max $maxdir    # ta da!
        unset counter        # ready for the next run
    else
        cd ..   # go up one level instead of "olddir"
    fi
}

It prints the max depth (including the starting directory as 1) and the first directory name that it finds at that depth. You can change the test if ((counter > max)) to >= and it will print the last directory name it finds at that depth.

Comments

4

Here's a one–liner that's pretty fast:

find . -type d -printf '%d:%p\n' | sort -n | tail -1

Or as a function:

depth() 
{ 
  find $1 -type d -printf '%d:%p\n' | sort -n | tail -1
}

Comments

3

Here is a version that seems to work:

#!/bin/sh

dir_depth() {
  cd "$1"
  maxdepth=0
  for d in */.; do
    [ -d "$d" ] || continue
    depth=`dir_depth "$d"`
    maxdepth=$(($depth > $maxdepth ? $depth : $maxdepth))
  done
  echo $((1 + $maxdepth))
}

dir_depth "$@"

4 Comments

This function counts the number of directories beneath its original target, including the target. I think that's what the question was...
Yeah, this is close to what I was trying to do. Thanks a lot for your help!
It counts one extra. Change the line to maxdepth=0 and it will count the target and the dirs below.
Unlike the original and mine, this doesn't attempt to output the directory name, which is why it's simpler.
1

The AIX (6.1) find command seems to be quite limited (e.g. no printf option). If you like to list all directories up to a given depth try this combination of find and dirname. Save the script code as maxdepth.ksh. In comparison to the Linux find -maxdepth option, AIX find will not stop at the given maximum level which results in a longer runtime, depending on the size/depth of the scanned direcory:

#!/usr/bin/ksh
# Param 1: maxdepth
# Param 2: Directoryname

max_depth=0
netxt_dir=$2
while [[ "$netxt_dir" != "/" ]] &&  [[ "$netxt_dir" != "." ]]; do
   max_depth=$(($max_depth + 1))
   netxt_dir=$(dirname $netxt_dir)
done

if [ $1 -lt $max_depth ]; then
   ret=1
else
   ret=0
   ls -d $2
fi
exit $ret

Sample call:

find /usr -type d -exec maxdepth.ksh 2 {} \;

Comments

0

The traditional way to do this is to have dir_depth return the maximum depth too. So you'll return both the name and depth.

You can't return an array, struct, or object in bash, so you can return e.g. a comma-separated string instead..

dir_depth(){ 
local dir

local max_dir="$1"
local max_depth=0

for dir in $1/*
do
  if [ -d "$dir" ]
  then
    cur_ret=$(dir_depth "$dir")
    cur_depth=$(expr "$cur_ret" : '\([^,]*\)')
    cur_dir=$(expr "$cur_ret" : '.*,\(.*\)')
    if [[ "$cur_depth" -gt "$max_depth" ]]; then
    max_depth="$cur_depth"
    max_dir="$cur_dir"
    fi
  fi
done
max_depth=$(($max_depth + 1))
echo "$max_depth,$max_dir"
}

EDIT: Fixed now. It starts with the directory you passed in as level 1, then counts upwards. I removed the cd, as it isn't necessary. Note that this will fail if filenames contain commas.

You might want to consider using a programming language with more built-in data structures, like Python.

3 Comments

Yeah, ideally I would have used Python for this, but I'm trying to learn Bash a little bit. I need to learn what you did with expr there, it looks interesting. Thanks for the help!
@Jef: Bash has regex matching that can be used in place of that of expr.
@Jef, I use this guide for string manipulation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.