Reputation: 167
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
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
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