开发者

Recursive Function to Return Directory Depth of File Tree

开发者 https://www.devze.com 2023-01-28 22:13 出处:网络
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

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开发者_如何学Python value. I'm trying to learn recursion better, but I'm not sure what I'm doing wrong.


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.


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
}


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 "$@"


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.


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 {} \;


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号