Linux 命令以树的形式打印目录结构
- 2024-10-21 09:13:00
- admin 原创
- 91
问题描述:
是否有任何 Linux 命令可以从 Bash 脚本中调用,以树的形式打印目录结构,例如,
folder1
a.txt
b.txt
folder2
folder3
解决方案 1:
这就是你要找的树吗?它应该在大多数发行版中(可能作为可选安装)。
~> tree -d /proc/self/
/proc/self/
|-- attr
|-- cwd -> /proc
|-- fd
| `-- 3 -> /proc/15589/fd
|-- fdinfo
|-- net
| |-- dev_snmp6
| |-- netfilter
| |-- rpc
| | |-- auth.rpcsec.context
| | |-- auth.rpcsec.init
| | |-- auth.unix.gid
| | |-- auth.unix.ip
| | |-- nfs4.idtoname
| | |-- nfs4.nametoid
| | |-- nfsd.export
| | `-- nfsd.fh
| `-- stat
|-- root -> /
`-- task
`-- 15589
|-- attr
|-- cwd -> /proc
|-- fd
| `-- 3 -> /proc/15589/task/15589/fd
|-- fdinfo
`-- root -> /
27 directories
样本取自维护者的网页。
您可以添加用数字替换的选项,以指定最大递归-L #
深度。#
删除-d
后也显示文件。
解决方案 2:
你可以使用这个:
ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^/]*//--/g' -e 's/^/ /' -e 's/-/|/'
它将在几秒钟内显示当前没有文件的子目录的图形表示,例如/var/cache/
:
.
|-apache2
|---mod_cache_disk
|-apparmor
|-apt
|---archives
|-----partial
|-apt-xapian-index
|---index.1
|-dbconfig-common
|---backups
|-debconf
来源
解决方案 3:
此命令用于显示文件夹和文件。
find . | sed -e "s/[^-][^/]*// |/g" -e "s/|([^ ])/|-/"
示例输出:
.
|-trace.pcap
|-parent
| |-chdir1
| | |-file1.txt
| |-chdir2
| | |-file2.txt
| | |-file3.sh
|-tmp
| |-json-c-0.11-4.el7_0.x86_64.rpm
来源: @javasheriff 的评论。它隐藏在评论中,并将其发布为答案有助于用户轻松发现它。
解决方案 4:
由于这是一条成功的评论,因此我将其添加为答案:
以树的形式打印目录结构,
使用文件
find . | sed -e "s/[^-][^/]*// |/g" -e "s/|([^ ])/|-/"
解决方案 5:
tree
由于我对其他(非)答案的输出不太满意(请参阅我对 Hassou 的回答的评论tree
),因此我尝试更多地模仿s 的输出。
它与罗伯特的答案类似,但水平线并非全部从开头开始,而是从应该开始的地方开始。perl
虽然不得不使用,但就我而言,在我没有的系统上tree
,perl
是可用的。
ls -aR | grep ":$" | perl -pe 's/:$//;s/[^-][^/]*// /g;s/^ (S)/└── /;s/(^ | (?= ))/│ /g;s/ (S)/└── /'
输出(缩短):
.
└── fd
└── net
│ └── dev_snmp6
│ └── nfsfs
│ └── rpc
│ │ └── auth.unix.ip
│ └── stat
│ └── vlan
└── ns
└── task
│ └── 1310
│ │ └── net
│ │ │ └── dev_snmp6
│ │ │ └── rpc
│ │ │ │ └── auth.unix.gid
│ │ │ │ └── auth.unix.ip
│ │ │ └── stat
│ │ │ └── vlan
│ │ └── ns
欢迎提出避免多余垂直线的建议:-)
我仍然非常喜欢Ben在 Hassou 的回答评论中的解决方案,没有(不完全正确的)线条,它更干净。对于我的用例,我还删除了全局缩进并添加了ls
隐藏文件的选项,如下所示:
ls -aR | grep ":$" | sed -e 's/:$//' -e 's/[^-][^/]*// /g'
输出(进一步缩短):
.
fd
net
dev_snmp6
nfsfs
rpc
auth.unix.ip
stat
vlan
ns
解决方案 6:
要将 Hassou 的解决方案添加到您的 .bashrc,请尝试:
alias lst='ls -R | grep ":$" | sed -e '"'"'s/:$//'"'"' -e '"'"'s/[^-][^/]*//--/g'"'"' -e '"'"'s/^/ /'"'"' -e '"'"'s/-/|/'"'"
解决方案 7:
当然,最好的答案是tree
。但是,为了改进依赖于 grepping 输出的其他答案ls -R
,这里有一个使用 awk 打印子目录树的 shell 脚本。首先,输出示例:
。
└── 匹配
│── 围兜
│ 数据
│ └── 来源
│ └── html
│ 数据
│ └── 情节
│ 方法
│ │ 信息
│ └── 柔软
│ │ ── 图片
│ │ │ │ │
│ │ └── 符号
│ └── js
└── 女士
然后,代码:
ls -qLR 2>/dev/null \n| grep '^./' \n| sed -e 's,:$,,' \n| awk '
function tip(new) { stem = substr(stem, 1, length(stem) - 4) new }
{
path[NR] = $0
}
END {
elbow = "└── "; pipe = "│ "; tee = "├── "; blank = " "
none = ""
#
# Model each stem on the previous one, going bottom up.
for (row = NR; row > 0; row--) {
#
# gsub: count (and clean) all slash-ending components; hence,
# reduce path to its last component.
growth = gsub(/[^/]+//, "", path[row]) - slashes
if (growth == 0) {
tip(tee)
}
else if (growth > 0) {
if (stem) tip(pipe) # if...: stem is empty at first!
for (d = 1; d < growth; d++) stem = stem blank
stem = stem elbow
}
else {
tip(none)
below = substr(stem, length(stem) - 4, 4)
if (below == blank) tip(elbow); else tip(tee)
}
path[row] = stem path[row]
slashes += growth
}
root = "."; print root
for (row = 1; row <= NR; row++) print path[row]
}
'
该代码给出的结果比其他解决方案更好看,因为在子目录树中,任何分支中的装饰都依赖于其下方的分支。因此,我们需要以相反的顺序处理输出ls -R
,从最后一行到第一行。
解决方案 8:
我正在美化@Hassou 的答案的输出:
ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^/]*//──/g' -e 's/─/├/' -e '$s/├/└/'
这很像tree
现在的输出:
.
├─pkcs11
├─pki
├───ca-trust
├─────extracted
├───────java
├───────openssl
├───────pem
├─────source
├───────anchors
├─profile.d
└─ssh
您还可以为其创建别名:
alias ltree=$'ls -R | grep ":$" | sed -e \'s/:$//\' -e \'s/[^-][^/]*//──/g\' -e \'s/─/├/\' -e \'$s/├/└/\''
顺便说一句,tree
在某些环境中不可用,例如 MinGW。因此替代方案很有帮助。
解决方案 9:
将现有答案合并并扩展为t
shell 函数
t() {
find -E "${1:-.}" -maxdepth "${2:-3}" \n -not -regex ".*/((.idea|.git|.venv|node_modules|venv)/.*|.DS_Store)" \n | sort | sed \n -e "s/[^-][^/]*// ├ /g" \n -e "s/├ //├ /g" \n -e "s/├ ├/│ ├/g" \n -e "s/├ ├/│ ├/g" \n -e "s/├ │/│ │/g" \n -e '$s/├/└/'
}
适用于Mac:
$ t
.
├ src
│ ├ .idea
│ ├ plugins
│ │ ├ .flake8
│ │ ├ .git
│ │ ├ .github
│ │ ├ .gitignore
│ │ ├ .pre-commit-config.yaml
│ │ ├ .python-version
│ │ ├ Makefile
│ │ ├ README.md
│ │ ├ buildspecs
│ │ ├ cicd
│ │ ├ cicd.py
│ │ ├ docker
│ │ ├ packages
│ │ ├ plugin_template
│ │ ├ plugins
│ │ ├ scripts
│ │ └ venv
$ t . 2
.
├ src
│ ├ .idea
│ └ plugins
$ t src/plugins/ | more
│ ├
│ ├ .flake8
│ ├ .git
│ ├ .github
│ │ ├ pull_request_template.md
│ ├ .gitignore
│ ├ .pre-commit-config.yaml
│ ├ .python-version
│ ├ Makefile
│ ├ README.md
│ ├ buildspecs
│ │ ├ test-and-deploy.yml
│ ├ cicd
:
| more
为了方便起见,可以将其放在函数的末尾。
解决方案 10:
在 bashrc 中添加以下函数可让您在不带任何参数的情况下运行命令,该命令将显示当前目录结构,并且当以任何路径作为参数运行时,将显示该路径的目录结构。这样就无需在运行命令之前切换到特定目录。
function tree() {
find ${1:-.} | sed -e "s/[^-][^/]*// |/g" -e "s/|([^ ])/|-/"
}
这在 gitbash 中也有效。
来源:@javasheriff 的评论
解决方案 11:
您还可以使用 find 和 awk 命令的组合来打印目录树。有关详细信息,请参阅“如何使用 linux find 和 awk 组合命令打印多级树形目录结构”
find . -type d | awk -F'/' '{
depth=3;
offset=2;
str="| ";
path="";
if(NF >= 2 && NF < depth + offset) {
while(offset < NF) {
path = path "| ";
offset ++;
}
print path "|-- "$NF;
}}'
解决方案 12:
我们可以使用以下命令
树
这将返回类似树的结构。
如果您想要整个目录直到最后一个子目录。您需要执行以下命令。
查找 .-type d
解决方案 13:
在 msa 的 perl 版本上进行扩展时,我添加了一些与默认 ls --color 选项相匹配的 ANSI 颜色编码输出。我还将文件重新添加到列表中。将其包装在 bash 函数中以供在 ~/.bashrc 中使用,并格式化为注释,现在看起来像这样:
function tree() {
find ${1:-.} | perl -pe '
chomp(); # Lose the line ending for file tests
$f=$_; # Keep full path in $f for file tests
if(-d $f) { $c="e[0;34m" } # Directories BLUE
elsif (-l $f) { $c="e[0;36m" } # Links CYAN
elsif (-X $f) { $c="e[0;32m" } # Executables GREEN
else { $c="e[0m" } # Else just RESET
s/[^-][^/]*// |/g; # Get rid of ./our/leading/path to just " |path"
s/|([^ ]+)/└──$c$1e[0m/; # Change |path to └──path and add $c coloring with reset
if (-d $f) { # If directory add trailing / too
s/$///
};
print("
"); # Because we chomp()ed the ending off at the start
END { print("
"); }' # One last line ending to keep it clean
}
对于这类恶作剧,我更喜欢使用 perl,因为:Perl 比 sed 更容易处理文件测试和 ANSI 转义序列。喜欢还是讨厌,Perl 几乎是所有 Unix 风格操作系统的基本安装。我喜欢 Python,但它对于我想扔进 ~/.bashrc 工具堆中的这种“单行”命令毫无用处。tree 命令很棒,但它并不经常安装,而且我发现在跨随机系统工作时,在我的 ~/.bashrc 咒语书中携带一堆魔法咒语更容易。
解决方案 14:
经过两个小时的反复试验,因为我不太擅长sed
或perl
。
无论如何,这是我的输出:
Makefile
ARGUMENTS := $(filter-out --,$(filter-out $(firstword $(MAKECMDGOALS)),$(MAKECMDGOALS)))
FIRST_ARGUMENT := $(word 1, $(ARGUMENTS))
SECOND_ARGUMENT := $(word 2, $(ARGUMENTS))
ifeq (tree, $(firstword $(MAKECMDGOALS)))
# use the rest as arguments for "run"
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
# ...and turn them into do-nothing targets
$(eval $(RUN_ARGS):;@:)
endif
# Tree command
tree:
@if [ "${FIRST_ARGUMENT}" = "dir" ]; then \n ls -aR ./${SECOND_ARGUMENT} | grep ":$$" | \n perl -pe 's/:$$//;s/[^-][^/]*// /g;s/^ (S)/ └─── /;s/(^ | (?= ))/ │ /g;s/ (S)/ └─── /'; \n elif [ "${FIRST_ARGUMENT}" = "file" ]; then \n find ./${SECOND_ARGUMENT} | sed -e "s/[^-][^/]*// │ /g" -e "s/│ ([^ ])/└─── /"; \n else \n exit 1; \n fi;
以下命令可能有用:
make tree dir source
意思是打印一棵仅由从./source
目录开始的目录组成的树。
输出:
> make tree dir source
└─── source
│ └─── bin
│ │ └─── gcd
│ │ └─── primetest
│ └─── libs
│ │ └─── GCDUtilities
│ │ └─── PrimalityUtilities
make: 'source' is up to date.
make tree file source
./source
此命令将以树的形式打印目录下的所有文件。
> make tree file source
└─── source
│ └─── bin
│ │ └─── gcd
│ │ │ └─── main.c
│ │ └─── primetest
│ │ │ └─── main.c
│ └─── libs
│ │ └─── GCDUtilities
│ │ │ └─── gcd.c
│ │ └─── PrimalityUtilities
│ │ │ └─── isprime.c
make: 'source' is up to date.
解决方案 15:
该tree
命令是您的最佳选择。
选择
如果您不是 sudoer 用户或无法安装,您可以使用利用和命令tree
的 bash 脚本。file
`stat`
tree
我尝试尽可能地接近命令的选项。
我甚至为档案、链接和可执行文件添加了突出显示。
#!/usr/bin/env bash
#==============================================================================
# TITLE : tree
# DESCRIPTION : A clone of the tree utility with additional features.
# AUTHOR : Mr. Polywhirl
# DATE : 2024-07-24
# VERSION : 1.0.0
# USAGE : tree [OPTIONS] [directory]
# NOTES : Requires the 'file' and 'stat' commands.
# BASH_VERSION : 4.2.46(2)-release
#==============================================================================
# DESCRIPTION:
# A clone of the tree utility with additional features such as
# displaying file sizes, sorting directories before files, and color highlighting.
#------------------------------------------------------------------------------
# USAGE:
# tree [OPTIONS] [directory]
#------------------------------------------------------------------------------
# OPTIONS:
# -s Print the size of each file in bytes.
# -h Print the size of each file in a human-readable way
# (e.g., appending a size letter for kilobytes (K), megabytes (M),
# gigabytes (G), terabytes (T), petabytes (P), and exabytes (E)).
# -L level Max display depth of the directory tree.
# -o file Send output to the specified file.
# --dirsfirst List directories before files.
# --help Display this help message.
#------------------------------------------------------------------------------
# EXAMPLES:
# Print the tree with file sizes in bytes:
# tree -s /path/to/directory
#
# Print the tree with file sizes in a human-readable way:
# tree -h /path/to/directory
#
# Print the tree with a maximum depth of 2:
# tree -L 2 /path/to/directory
#
# Print the tree with directories listed before files:
# tree --dirsfirst /path/to/directory
#------------------------------------------------------------------------------
# INSTALLATION:
# 1. Save this script to a file named '~/.scripts/tree'.
# 2. Make the script executable with the command 'chmod u+x ~/.scripts/tree'.
# 3. Add the directory '~/.scripts' to your PATH environment variable.
#==============================================================================
# Define ANSI color codes
RESET='[0m'
RED='[0;91m'
GREEN='[0;92m'
BLUE='[1;94m'
CYAN='[1;96m'
# Define a list of MIME types for compressed archives
ARCHIVE_MIME_TYPES=(
"application/zip" # .zip
"application/x-tar" # .tar
"application/gzip" # .gz
"application/x-bzip2" # .bz2
"application/x-xz" # .xz
"application/x-7z-compressed" # .7z
"application/x-rar" # .rar
"application/java-archive" # .jar
"application/x-archive" # .ar
"application/vnd.android.package-archive" # .apk
"application/vnd.debian.binary-package" # .deb
"application/x-redhat-package-manager" # .rpm
"application/vnd.microsoft.portable-executable" # .exe
"application/vnd.ms-cab-compressed" # .cab
)
# Default values for options
print_size=false
max_depth=-1
human_readable_size=false
output_file=""
use_color=true
dirs_first=false
highlight() {
local name=$1
local color=$2
if $use_color; then
echo -e "${color}${name}${RESET}"
else
echo "$name"
fi
}
highlight_link() {
local name=$1
local target=$2
if $use_color; then
echo -e "${CYAN}${name}${RESET} -> $(highlight_item "$target")"
else
echo "$name -> $target"
fi
}
highlight_item() {
local item=$1
local display_name
display_name=$(basename "$item")
if [ -d "$item" ]; then
highlight "$display_name" "$BLUE"
elif [ -x "$item" ]; then
highlight "$display_name" "$GREEN"
elif is_archive "$item"; then
highlight "$display_name" "$RED"
else
echo "$display_name"
fi
}
is_archive() {
local item=$1
local mime_type
mime_type=$(file --mime-type -b "$item")
for type in "${ARCHIVE_MIME_TYPES[@]}"; do
if [[ "$mime_type" == "$type" ]]; then
return 0
fi
done
return 1
}
get_human_readable_size() {
local size=$1
local units=("B" "K" "M" "G" "T" "P" "E")
local i=0
while [ "$(echo "$size >= 1024" | bc)" -eq 1 ] && [ $i -lt ${#units[@]} ]; do
size=$(echo "$size / 1024" | bc -l)
size=$(printf "%.0f" "$size") # Convert to integer for the next iteration
i=$((i + 1))
done
printf "%.1f%s" "$size" "${units[$i]}"
}
get_size() {
local item=$1
local size
size=$(stat -c%s "$item")
if [ "$human_readable_size" = true ]; then
echo " ($(get_human_readable_size "$size"))"
else
echo " ($size bytes)"
fi
}
get_display_name() {
local item=$1
local display_name
local size_info=""
display_name=$(basename "$item")
if [ "$print_size" = true ]; then
size_info=$(get_size "$item")
fi
if [ -d "$item" ]; then
display_name=$(highlight "$display_name" "$BLUE")
elif [ -L "$item" ]; then
local target
target=$(readlink "$item")
display_name=$(highlight_link "$display_name" "$target")
else
display_name=$(highlight_item "$item")
fi
echo "$display_name$size_info"
}
print_tree() {
local dir=$1
local prefix=$2
local depth=$3
local output=$4
# Stop if max depth is reached
if [ "$max_depth" -ne -1 ] && [ "$depth" -ge "$max_depth" ]; then
return
fi
# List all files and directories in the current directory
local items=("$dir"/*)
# Handle empty directories
if [ ! -e "${items[0]}" ]; then
echo "${prefix}(empty)" >> "$output"
return
fi
if $dirs_first; then
# Separate directories and files
local dirs=()
local files=()
for item in "${items[@]}"; do
if [ -d "$item" ]; then
dirs+=("$item")
else
files+=("$item")
fi
done
items=("${dirs[@]}" "${files[@]}")
fi
local last_index=$((${#items[@]} - 1))
for i in "${!items[@]}"; do
local item="${items[$i]}"
local new_prefix="${prefix}│ "
# Determine the correct prefix for the next level
if [ "$i" -eq $last_index ]; then
new_prefix="${prefix} "
fi
# Get the display name with colors
local display_name
display_name=$(get_display_name "$item")
# Print the current item
echo -e "${prefix}└── ${display_name}" >> "$output"
# Recursively print the tree for directories
if [ -d "$item" ]; then
print_tree "$item" "$new_prefix" $((depth + 1)) "$output"
fi
done
}
print_help() {
echo "Usage: $(basename "$0") [OPTIONS] [directory]"
echo ""
echo "Options:"
echo " -s Print the size of each file in bytes"
echo " -h Print the size of each file in a human-readable way"
echo " (e.g., appending a size letter for kilobytes (K), megabytes (M),"
echo " gigabytes (G), terabytes (T), petabytes (P), and exabytes (E))"
echo " -L level Max display depth of the directory tree"
echo " -o file Send output to the specified file"
echo " --dirsfirst List directories before files"
echo " --help Display this help message"
}
# Parse options
while getopts "sho:L:-:" opt; do
case $opt in
s) print_size=true ;;
h) print_size=true; human_readable_size=true ;;
L) max_depth=$OPTARG ;;
o) output_file=$OPTARG; use_color=false ;;
-)
case $OPTARG in
help) print_help; exit 0 ;;
dirsfirst) dirs_first=true ;;
*) echo "Invalid option: --$OPTARG"; print_help; exit 1 ;;
esac ;;
*) echo "Invalid option: -$OPTARG"; print_help; exit 1 ;;
esac
done
shift $((OPTIND - 1))
# Check if a directory is passed as an argument, otherwise use the current directory
if [ $# -eq 0 ]; then
dir="."
else
dir="$1"
fi
# Initialize the output file or standard output
if [ -n "$output_file" ]; then
echo "." > "$output_file"
else
output_file="/dev/stdout"
echo "."
fi
# Start the tree printing
print_tree "$dir" "" 0 "$output_file"
这是一个更简单的版本,没有选项或突出显示。
#!/usr/bin/env bash
#==============================================================================
# TITLE : tree
# DESCRIPTION : A simple clone of the tree utility.
# AUTHOR : Mr. Polywhirl
# DATE : 2024-07-24
# VERSION : 1.0.0
# USAGE : tree [directory]
# NOTES : Requires bash version 4.2.46(2)-release or higher.
# BASH_VERSION : 4.2.46(2)-release
#==============================================================================
# DESCRIPTION:
# A simple clone of the tree utility that prints the directory structure.
#------------------------------------------------------------------------------
# USAGE:
# tree [directory]
#------------------------------------------------------------------------------
# EXAMPLES:
# Print the tree structure for the current directory:
# tree
#
# Print the tree structure for a specified directory:
# tree /path/to/directory
#==============================================================================
# INSTALLATION:
# 1. Save this script to a file named '~/.scripts/tree'.
# 2. Make the script executable with the command 'chmod u+x ~/.scripts/tree'.
# 3. Add the directory '~/.scripts' to your PATH environment variable.
#==============================================================================
print_tree() {
local dir=$1
local prefix=$2
# List all files and directories in the current directory
local items=("$dir"/*)
local last_index=$((${#items[@]} - 1))
for i in "${!items[@]}"; do
local item="${items[$i]}"
local new_prefix="${prefix}│ "
# Determine the correct prefix for the next level
if [ "$i" -eq $last_index ]; then
new_prefix="${prefix} "
fi
# Get the display name
local display_name
display_name=$(basename "$item")
# Print the current item
if [ -d "$item" ]; then
echo "${prefix}└── ${display_name}/"
# Recursively print the tree for directories
if [ "$(ls -A "$item")" ]; then
print_tree "$item" "$new_prefix"
fi
else
echo "${prefix}└── ${display_name}"
fi
done
}
# Check if a directory is passed as an argument, otherwise use the current directory
if [ $# -eq 0 ]; then
dir="."
else
dir="$1"
fi
# Print the root directory
echo "."
# Start the tree printing
print_tree "$dir" ""
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件