1

I have to search all subdirs recursively and print *(number of * = depth of file/dir) type and name. The problem comes when i enter dir and then want to get out but nothing happens.

my test file

DIR test
*FILE ace
*FILE base
*DIR father
**FILE cookies
*DIR mother
**DIR how
***FILE youdoing
*FILE zebra

my code

maxDepth is how far in to the dir it can go(default 3) and currDepth is 1 at the beginning

    function tree(){
        maxDepth=$2
        currDepth=$3
        #print the starting file
        if [ "$currDepth" -eq 0 ];then
            printf  "%s   %s\n" DIR "$1"
            currDepth=1
        fi
        for path in "$1"/*;do
            for i in $( seq 1 $currDepth );do       
                echo -n *
            done
            if [ -d "$path" ];then
                printf  "%s   %s\n" DIR "${path##*/}"           
                if [[ "$currDepth" -lt "$maxDepth" ]];then
                    tree "$path" "$maxDepth" "$(( currDepth + 1 ))"
                fi
                continue
            fi
            if [ -f "$path" ];then      
                printf "%s  %s\n" FILE "${path##*/}"
                continue
            fi
            if [ -L "$path" ];then
                printf "%s  %s\n" LINK "${path##*/}"
                continue            
            fi
       done
  }

my output

DIR test
*FILE ace
*FILE base
*DIR father
**FILE cookies
**DIR mother
***DIR how
***FILE zebra

what am i doing wrong

5
  • 1
    One thing you're doing wrong is you should use the find command Commented Apr 19, 2014 at 22:33
  • It specifically says i should use recursion Commented Apr 19, 2014 at 22:50
  • 1
    This function isn't recursive. It doesn't call tree, it just calls printf tree …. Commented Apr 19, 2014 at 23:09
  • Don't know how that printf sneaked in there Commented Apr 19, 2014 at 23:24
  • Question:what do you want to do with it? is it just to learn recursion in bash or do you have something else in mind? Commented Apr 19, 2014 at 23:40

3 Answers 3

1
  1. Debug your script by doing set -x before you run it.
  2. Make sure integers are always integers by declaring them with the -i integer attribute.
  3. Use expression syntax consistently. It's a good idea to always use [[ ]] tests for string comparisons and (( )) for arithmetic and numeric comparisons, if your target shell is bash.
  4. Use (( )) for loops instead of seq, which is nonstandard.
  5. Explicitly declare your variables in the function (using local or declare) to ensure they are scoped to the function.
  6. Actually call the inner tree.

#!/bin/bash
tree() {
    local -i maxDepth=$2 # Make sure these values are always integers
    local -i currDepth=$3

    # print the starting file
    if (( currDepth == 0 )); then # use bash arithmetic
        printf  "%s   %s\n" DIR "$1"
        currDepth=1
    fi
    for path in "$1"/*;do
        for ((i=0; i<currDepth; i++)); do
            printf '*'
        done
        if [[ -d "$path" ]];then
            printf  "%s   %s\n" DIR "${path##*/}"           
            if [[ "$currDepth" -lt "$maxDepth" ]];then
                tree "$path" "$maxDepth" "$(( currDepth + 1 ))"
            fi
            continue
        fi
        if [[ -f "$path" ]];then      
            printf "%s  %s\n" FILE "${path##*/}"
            continue
        fi
        if [[ -L "$path" ]];then
            printf "%s  %s\n" LINK "${path##*/}"
            continue            
        fi
   done
}
Sign up to request clarification or add additional context in comments.

5 Comments

Fixed the syntax but it seems something is wrong with recursion. Like it doesn't even return to the start file, plus currDepth never changes back to 1
@user2202368 : 1. Debug your script by doing set -x before you run it.. Did you try that? Good luck.
I'm doing right now, these continue-s are suspicious
there's a problem with currDepth, it doesn't change back, any suggestions
Yes, use the local keyword to make sure that your variables are scoped to the function. I've added that to the answer.
1

Here a solution using find, stat and sed:

find <DIR> -exec stat --printf="%n,%F\n" "{}" \; | \
 sed -r -e "s/[^\/]+\//\*/g" -e "s/regular file/FILE/" -e "s/directory/DIR/" | \
 sed -r -e "s/([\*]+)([^,]+),(.+)/\1 \3 \2/"

IMPORTANT: Use DIR not DIR/ otherwise DIR name will not appear in results.

Explanation:

find returns recursively all files and directory within DIR.

-exec option in find allows to pass each result to another command. Here I'm passing each result to the command stat

stat has an option to format the output -printf (see manpage) :

  • %n is the filename (with relavtive path)
  • %F is the file type ( regular file, directory,symbolic link,block special file...)

So,

find <DIR> -exec stat --printf="%n,%F\n" "{}" \;

returns the following, one result by line (assuming that there are only regular files and directories in DIR) :

DIR/path/to/file,regular file
DIR/path/to/dir,directory

Then, I'm using sed to transform each line the way you required using regular expression:

  1. Replace string/ by * -> ***basename,file type
  2. Replace "regular file" by FILE
  3. Replace "directory" by DIR
  4. Shuffle around basename and filetype using back referencing in sed.

NOTE: I will not explain in details how regular expressions work as it would be too long.

3 Comments

I have no f**king idea what you did here, care to explain
@user2202368 you basically told us in comments to your question that you didn't want an answer using find in lieu of a recursive function. Then you went and accepted an answer using find instead of a recursive function. What's up with that?
Changed the answer, i discovered the meaning of the word local :D
0

I should have used local in front of currDepth=$3

Comments

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.