neziric
neziric

Reputation: 167

Creating directories and files from TREE command output

I've used a tree command to list recursively about a hundred folders with some 150 files in them. This tree output is saved in a file.

How can I parse this file via bash and recreate these files and folders on a different computer?

Note that I don't need to copy these files. All I need is just the same naming convention on another computer where some custom work will be done.

Upvotes: 5

Views: 2689

Answers (2)

Scott VR
Scott VR

Reputation: 1

I know it has been over a decade, but I found this question by explicitly searching to see if there was anyone out there who had asked such a question.

[EDIT] ok.. I know it has now been over four hours since I answered this question, but since doing so I have been bothered by the fact that the question explicitly asked for a "bash" solution, and while I think my answer originally did meet that constraint, I felt like my referring the OP to a python script was in the wrong spirit so... Now, in addition to the python and typescript versions, here is a a "pure" bash solution (so long as you consider standard utilities such as sed, and tr to be, well, standard on a system that is running bash.

TL;DR: I am linking to a tool I created that does this, and because of this question being tagged "bash", said tool now has a bash version, written solely because of this question. https://github.com/scottvr/eert/

#!/bin/bash

count_indent() {
    local line="$1"
    local tree_chars
    tree_chars=$(echo "$line" | grep -o '[│├└─ ]' | tr -d '\n')
    echo "${#tree_chars}"
}

clean_line() {
    local line="$1"
    echo "$line" | sed 's/[│├└─]//g' | sed 's/^ *//' | sed 's/ *$//'
}

check_path() {
    local path="$1"
    local name="$2"
    
    # Check full path length (4096 is common max path length for many filesystems)
    if [[ ${#path} -gt 4096 ]]; then
        printf "Skip: Path too long (%d chars): ...%s\n" "${#path}" "${path: -50}" >&2
        return 1
    fi
    
    # Check individual component length (255 is common max for many filesystems)
    if [[ ${#name} -gt 255 ]]; then
        printf "Skip: Component too long (%d chars): ...%s\n" "${#name}" "${name: -50}" >&2
        return 1
    fi
    
    return 0
}

create_from_tree() {
    local root_dir="${1:-.}"
    local current_path="$root_dir"
    
    declare -a paths=("$root_dir")
    declare -a indents=(-1)
    
    while IFS= read -r line || [[ -n "$line" ]]; do
        [[ -z "$(echo "$line" | tr -d '[:space:]')" ]] && continue

        local current_indent=$(($(count_indent "$line") / 2))
        local cleaned_line
        cleaned_line=$(clean_line "$line")
        
        while [[ ${indents[-1]} -ge $current_indent ]]; do
            unset 'paths[-1]'
            unset 'indents[-1]'
        done
        
        current_path="${paths[-1]}"
        
        if [[ "$cleaned_line" == */ ]]; then
            cleaned_line="${cleaned_line%/}"
            new_dir_path="$current_path/$cleaned_line"
            
            if check_path "$new_dir_path" "$cleaned_line"; then
                mkdir -p "$new_dir_path"
                paths+=("$new_dir_path")
                indents+=("$current_indent")
            fi
        else
            cleaned_line="${cleaned_line%\*}"
            new_file_path="$current_path/$cleaned_line"
            
            if check_path "$new_file_path" "$cleaned_line"; then
                touch "$new_file_path"
            fi
        fi
    done
}

create_from_tree "$1"

original answer below:

git clone https://github.com/scottvr/eert/
cd eert/eert-python
# copy a structure (but not file contents) from some example directory:
tree -F /home/user/example | python eert.py
# or from a file copied from a chat, document, etc:
$ mkdir test && cd test
$ tree -F
./

0 directories, 0 files
$ python ../eert.py < ../example_tree.txt
$ tree -F
./
└── eert_example/
    ├── one/
    │   ├── 1file.py*
    │   ├── 2file.txt*
    │   └── 3file*
    ├── threedom/
    └── two/
        ├── blah.py*
        ├── bleh.py*
        └── somedir/

6 directories, 5 files

I find this question's existence (and lack of fully correct answer) both funny and heart-warming, and the responses cause me to wonder if one factor keeping more people from asking such a question is the obvious self-answer of "well, the output of tree is meant for humans."

And while that is certainly correct, it hasn't - for me - stopped the question from popping into my head several times (over many years, but still.) For example, what if OP did not have access to a filesystem like the answer using the find command requires, but like OP stated "This tree output is saved in a file" and the user wants to parse this file, which those find commands will not do.

So... I ended up spending way more time than expected or intended solving this problem, so the time-value/cost-benefit ratios are not in favor of the resulting tool. As such, I thought that since I will never likely correct that value imbalance with my own usage, that I would set about to see if anyone else had searched for a solution to the problem of parsing tree output from some document, and creating the directories and (empty) files represented in this tree on a filesystem.

So, in the interest of retroactively recouping the time/effort spent with collective time/effort saved, I would like to present this new tool, which you can run from bash, or even from vscode (I already admitted to spending too much time on this): https://github.com/scottvr/eert

Upvotes: 0

likso
likso

Reputation: 3440

find will do what you want, something like

find /my/pathto/blah -type d | sed -e "s/^/mkdir -p /g" > commands
find /my/pathto/blah -type f | sed -e "s/^/touch /g" >> commands

The first find will create the instructions to make the directories. The second find will create the instructions to create empty files.

Upvotes: 6

Related Questions