[!前言]
对于经常写笔记或者写文档得同学来说,经常会需要获取一个项目工程下得目录结构,那么如何在不需要下载或者安装插件来达到这个目的呢?

示例

只含有三级目录

包含文件得四级文件树

指定忽略部分文件或者文件夹,比如target目录,logs目录

image.png

使用方法

新建脚本

在你需要输出目录结构得文件夹下新建.sh脚本,名称随意,比如叫tree.sh
image.png

输入脚本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/bin/bash

# 获取当前脚本的名称
script_name=$(basename "$0")
output_file="tree_output.txt"

# 定义读取 .gitignore 文件的函数
read_gitignore() {
local directory="$1"
gitignore_file="$directory/.gitignore"
if [[ -f "$gitignore_file" ]]; then
# 读取 .gitignore 文件并排除注释和空行
GITIGNORE_RULES=($(grep -Ev '^\s*#|^\s*$' "$gitignore_file"))
else
GITIGNORE_RULES=()
fi
# 将脚本名称和输出文件添加到排除列表
GITIGNORE_RULES+=("$script_name" "$output_file")
}

# 检查文件或文件夹是否应该被忽略
is_excluded() {
local path="$1"
local relative_path="${path#$directory/}"
local match_exclude=0 # 是否匹配到排除规则
local match_include=0 # 是否匹配到包含规则(例外)

# 转换为相对路径,并确保路径不以斜杠开头
relative_path=$(echo "$relative_path" | sed 's/^\///')

# 遍历 .gitignore 规则
for rule in "${GITIGNORE_RULES[@]}"; do
local original_rule="$rule"
local is_inclusion=0 # 默认为排除规则
if [[ "${rule:0:1}" == "!" ]]; then
is_inclusion=1 # 如果是例外规则
rule="${rule:1}" # 移除开头的感叹号
fi

# 替换通配符 * 和 ?
local pattern="${rule//\*/.*}"
pattern="${pattern//\?/.}"

# 根据规则中的斜杠位置构建不同的正则表达式
if [[ "$rule" == /* ]]; then
pattern="${pattern#/}"
pattern="^$pattern($|/.*)$"
elif [[ "$rule" == */ ]]; then
pattern="${pattern%/}"
pattern="^$pattern(/.*|$)"
elif [[ "$rule" == */* ]]; then
pattern="($|^.*/)$pattern($|/.*)$"
else
pattern="($|^.*/)$pattern($|/.*)$"
fi

# 检查路径匹配
if [[ "$relative_path" =~ $pattern ]]; then
if [[ "$is_inclusion" -eq 1 ]]; then
match_include=1 # 匹配到例外规则,标记为包含
else
match_exclude=1 # 匹配到排除规则,标记为排除
fi
fi
done

# 如果匹配到包含规则,返回0(不忽略)
# 如果匹配到排除规则且没有匹配到包含规则,返回1(忽略)
if [[ "$match_include" -eq 1 ]]; then
return 0
elif [[ "$match_exclude" -eq 1 ]]; then
return 1
else
return 0 # 默认不忽略
fi
}

# 定义全局数组来记录每层级的计数
declare -a count_stack
declare -a index_stack

# 定义递归函数来打印目录结构
print_tree() {
local directory="$1"
local prefix="$2"
local exclude_files="$3"
local level="$4"
local max_level="$5"

# 递归到达最大层级时返回
if [[ -n "$max_level" && "$level" -gt "$max_level" ]]; then
return
fi

# 获取当前目录下的所有文件和文件夹
shopt -s nullglob
local items=("$directory"/*)
shopt -u nullglob
local count=${#items[@]}

count_stack[$level]=$count
index_stack[$level]=0

while [[ ${index_stack[$level]} -lt ${count_stack[$level]} ]]; do
local item="${items[${index_stack[$level]}]}"
local base_name=$(basename "$item")
local is_last=$((index_stack[$level] == count - 1))

index_stack[$level]=$((index_stack[$level] + 1))

# 跳过应该被 .gitignore 排除的文件夹和文件
if [[ "$apply_gitignore" == "y" ]]; then
is_excluded "$item"
if [[ $? -eq 1 ]]; then # 检查 is_excluded 的返回值,如果为 1 则忽略
continue
fi
fi


# 确定当前项的前缀符号
if [ "$is_last" == "1" ]; then
current_prefix="${prefix}└── $base_name"
new_prefix="${prefix} "
else
current_prefix="${prefix}├── $base_name"
new_prefix="${prefix}│ "
fi

if [ -d "$item" ]; then
# 打印目录名
echo "$current_prefix"
# 保存目录名到输出变量
output_tree+="$current_prefix"$'\n'
# 递归调用自己
print_tree "$item" "$new_prefix" "$exclude_files" $((level + 1)) "$max_level"
elif [ "$exclude_files" != "y" ]; then
# 打印文件名
echo "$current_prefix"
# 保存文件名到输出变量
output_tree+="$current_prefix"$'\n'
fi
done
}

# 获取当前目录的树型结构
directory="$(pwd)"

# 询问用户是否应用 .gitignore 中的内容
read -p "是否应用 .gitignore 中的内容? (y/n): " apply_gitignore

# 如果选择不应用,则清空排除列表
if [ "$apply_gitignore" == "n" ]; then
GITIGNORE_RULES=()
else
# 读取 .gitignore 文件
read_gitignore "$directory"
fi

# 询问用户是否不包含文件
read -p "输出的树是否不包含文件? (y/n): " exclude_files

# 询问用户输出的层级
read -p "请输入输出的层级 (默认输出所有层级): " max_level

# 初始化输出变量
output_tree=""

# 打印并保存树型结构
echo "$(basename "$directory")"
output_tree+="$(basename "$directory")"$'\n'
print_tree "$directory" "" "$exclude_files" 1 "$max_level"

# 询问用户是否导出树型结构
read -p "是否导出这个输出的树? (y/n): " export_tree

if [ "$export_tree" == "y" ]; then
echo -e "$output_tree" > "$output_file"
echo "树型结构已导出到 $output_file"
fi

双击运行

运行过程中会有需要交互得地方

是否应用 .gitignore 中的内容?

我们知道git提交会配置.gitignore来忽略不想提交得内容,spring boot中得target目录、logs目录等,前端得.vscode目录等,我们并不想将这些内容输出到文件树中,就在第一个用户交互得地方输入y
image.png

输出的树是否不包含文件?

如果输入y则会将所有文件输出到结果得树中。
image.png

请输入输出的层级 (默认输出所有层级):

默认为所有层级,输入数字则输出数字对应得层级
image.png

是否导出这个输出的树? (y/n)

最后是导出这个树到txt文件中,便于复制使用
image.png
image.png
image.png