JeyKey
JeyKey

Reputation: 423

How to implement tree command in bash/shell script?

I tried to implement in bash some code which output would be similar to the "tree" command in the terminal.

Here it is:

listContent() {
    local file
    for file in "$1"/*; do
        if [ -d $file ]; then
            countSpaces $file
            echo $?"Directory: $file"
            listContent "$file"
        elif [ -f $file ]; then
            countSpaces $file
            echo $?"File: $file"
        fi
    done
}

countSpaces() {
    local space="   "
    for (( i=0; i<${#$1}; i++ )); do
        if [ ${file:$i:1} = "/" ]; then
        space = space + space
        return space
    done
}

listContent "$1"

Running the script I give: ./scriptName.sh directoryName where scriptName is my script and directoryName is the argument which is the name of the directory from which the code should start.

I would like to see the output like this:

Directory: Bash/Test1Dir
    File: Bash/Test1Dir/Test1Doc.txt
Directory: Bash/Test2Dir
    Directory: Bash/Test2Dir/InsideTest2DirDir
        File: Bash/Test2Dir/insideTest2DirDoc.txt
File: Bash/test.sh

But I have some troubles in completing this code. Could someone help me figure it out why it isn't working and what should I change?

Will be grateful.

Upvotes: 1

Views: 4325

Answers (2)

go2null
go2null

Reputation: 2298

if you want a visual representation without the Directory and File leaders, then the following is a simple one-liner (wrapped in a shell function).

treef() (
    [ -d "$1" ] && { dir="$1"; shift; } || dir='.'
    find "$dir" "$@" | sed -e 's@/@|@g;s/^\.|//;s/[^|][^|]*|/ |/g;/^[. |]*$/d'
)

Upvotes: 0

Charles Duffy
Charles Duffy

Reputation: 295473

A correct and efficient implementation might look like:

listContent() {
  local dir=${1:-.} whitespacePrefix=$2 file
  for file in "$dir"/*; do
    [ -e "$file" ] || [ -L "$file" ] || continue
    if [ -d "$file" ]; then
      printf '%sDirectory %q\n' "$whitespacePrefix" "${file##*/}"
      listContent "$file" "${whitespacePrefix}    "
    else
      printf '%sFile %q\n' "$whitespacePrefix" "${file##*/}"
    fi
  done
}

Note:

  • Instead of counting spaces, we use the call stack to track the amount of whitespace, appending on each recursive call. This avoids needing to count the number of /s in each name.
  • We quote all parameter expansions, except in one of the limited number of contexts where string-splitting and glob expansions are implicitly avoided.
  • We avoid attempts to use $? for anything other than its intended purpose of tracking numeric exit status.
  • We use printf %q whenever uncontrolled data (such as a filename) is present, to ensure that even malicious names (containing newlines, cursor-control characters, etc) are printed unambiguously.

Upvotes: 5

Related Questions