如何编写树形展示项目目录结构脚本

wen 实用脚本 28

本文目录导读:

如何编写树形展示项目目录结构脚本

  1. 纯Python实现(跨平台)
  2. Bash脚本实现(Linux/Unix)
  3. Node.js实现
  4. 简单实用的Python版本
  5. 使用示例

纯Python实现(跨平台)

#!/usr/bin/env python3
import os
import sys
from pathlib import Path
def tree(directory, prefix="", max_depth=None, current_depth=0):
    """
    递归生成树形目录结构
    """
    if max_depth is not None and current_depth > max_depth:
        return
    path = Path(directory)
    # 获取目录下的所有项
    try:
        items = list(path.iterdir())
    except PermissionError:
        print(f"{prefix}└── [权限拒绝] {directory}")
        return
    # 排序:目录在前,文件在后
    items.sort(key=lambda x: (not x.is_dir(), x.name.lower()))
    # 处理最后一项的特殊前缀
    for i, item in enumerate(items):
        is_last = i == len(items) - 1
        # 选择连接线
        if is_last:
            connector = "└── "
            new_prefix = prefix + "    "
        else:
            connector = "├── "
            new_prefix = prefix + "│   "
        # 打印当前项
        if item.is_dir():
            print(f"{prefix}{connector}📁 {item.name}/")
            tree(item, new_prefix, max_depth, current_depth + 1)
        else:
            # 显示文件大小
            try:
                size = item.stat().st_size
                size_str = format_size(size)
                print(f"{prefix}{connector}📄 {item.name} ({size_str})")
            except OSError:
                print(f"{prefix}{connector}📄 {item.name}")
def format_size(size):
    """格式化文件大小"""
    for unit in ['B', 'KB', 'MB', 'GB']:
        if size < 1024:
            return f"{size:.1f} {unit}"
        size /= 1024
    return f"{size:.1f} TB"
def main():
    import argparse
    parser = argparse.ArgumentParser(description='显示目录树形结构')
    parser.add_argument('path', nargs='?', default='.', help='要显示的目录路径')
    parser.add_argument('-d', '--max-depth', type=int, help='最大显示深度')
    parser.add_argument('--no-size', action='store_true', help='不显示文件大小')
    args = parser.parse_args()
    if not os.path.exists(args.path):
        print(f"错误:路径 '{args.path}' 不存在")
        sys.exit(1)
    if not os.path.isdir(args.path):
        print(f"错误:'{args.path}' 不是一个目录")
        sys.exit(1)
    print(f"📁 {os.path.abspath(args.path)}/")
    tree(args.path, max_depth=args.max_depth)
if __name__ == "__main__":
    main()

Bash脚本实现(Linux/Unix)

#!/bin/bash
# 颜色定义
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
show_tree() {
    local dir="$1"
    local prefix="$2"
    local max_depth="$3"
    local current_depth="$4"
    # 深度检查
    if [[ -n "$max_depth" && "$current_depth" -gt "$max_depth" ]]; then
        return
    fi
    # 获取项目列表
    local items=("$dir"/*)
    local total=${#items[@]}
    local count=0
    for item in "${items[@]}"; do
        # 跳过没有权限的文件
        [[ ! -r "$item" ]] && continue
        count=$((count + 1))
        local is_last=false
        [[ $count -eq $total ]] && is_last=true
        # 选择连接线
        if $is_last; then
            local connector="└── "
            local new_prefix="${prefix}    "
        else
            local connector="├── "
            local new_prefix="${prefix}│   "
        fi
        # 获取文件名
        local name=$(basename "$item")
        if [[ -d "$item" ]]; then
            echo -e "${prefix}${connector}${BLUE}📁 ${name}/${NC}"
            show_tree "$item" "$new_prefix" "$max_depth" $((current_depth + 1))
        else
            # 获取文件大小
            local size=$(du -h "$item" 2>/dev/null | cut -f1)
            echo -e "${prefix}${connector}${GREEN}📄 ${name}${NC} ${YELLOW}(${size})${NC}"
        fi
    done
}
# 主函数
main() {
    local path="${1:-.}"
    local max_depth="${2:-}"
    # 检查路径是否存在
    if [[ ! -e "$path" ]]; then
        echo "错误:路径 '$path' 不存在"
        exit 1
    fi
    # 获取绝对路径
    local abs_path=$(cd "$path" && pwd)
    echo -e "${BLUE}📁 ${abs_path}/${NC}"
    show_tree "$path" "" "$max_depth" 0
}
# 使用示例
if [[ $# -eq 0 ]]; then
    echo "用法: $0 <目录路径> [最大深度]"
    echo "示例: $0 /home/user/project 3"
    exit 0
fi
main "$@"

Node.js实现

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
class TreeGenerator {
    constructor(rootPath, options = {}) {
        this.rootPath = rootPath;
        this.maxDepth = options.maxDepth || Infinity;
        this.showHidden = options.showHidden || false;
        this.showSize = options.showSize !== false;
    }
    generate() {
        const absPath = path.resolve(this.rootPath);
        console.log(`📁 ${absPath}/`);
        this._traverse(absPath, '');
    }
    _traverse(dirPath, prefix, depth = 0) {
        if (depth > this.maxDepth) return;
        try {
            const items = fs.readdirSync(dirPath, { withFileTypes: true });
            // 过滤隐藏文件
            const filtered = this.showHidden ? 
                items : items.filter(item => !item.name.startsWith('.'));
            // 排序:目录在前
            filtered.sort((a, b) => {
                if (a.isDirectory() !== b.isDirectory()) {
                    return a.isDirectory() ? -1 : 1;
                }
                return a.name.localeCompare(b.name);
            });
            filtered.forEach((item, index) => {
                const isLast = index === filtered.length - 1;
                const connector = isLast ? '└── ' : '├── ';
                const newPrefix = isLast ? `${prefix}    ` : `${prefix}│   `;
                const itemPath = path.join(dirPath, item.name);
                if (item.isDirectory()) {
                    console.log(`${prefix}${connector}📁 ${item.name}/`);
                    this._traverse(itemPath, newPrefix, depth + 1);
                } else {
                    if (this.showSize) {
                        const stats = fs.statSync(itemPath);
                        const size = this._formatSize(stats.size);
                        console.log(`${prefix}${connector}📄 ${item.name} (${size})`);
                    } else {
                        console.log(`${prefix}${connector}📄 ${item.name}`);
                    }
                }
            });
        } catch (err) {
            console.error(`${prefix}└── [错误] ${err.message}`);
        }
    }
    _formatSize(bytes) {
        const units = ['B', 'KB', 'MB', 'GB', 'TB'];
        let size = bytes;
        let unitIndex = 0;
        while (size >= 1024 && unitIndex < units.length - 1) {
            size /= 1024;
            unitIndex++;
        }
        return `${size.toFixed(1)} ${units[unitIndex]}`;
    }
}
// 命令行接口
if (require.main === module) {
    const args = process.argv.slice(2);
    const options = {};
    let targetPath = '.';
    args.forEach((arg, index) => {
        if (arg === '-d' || arg === '--depth') {
            options.maxDepth = parseInt(args[index + 1]);
        } else if (arg === '-a' || arg === '--all') {
            options.showHidden = true;
        } else if (arg === '--no-size') {
            options.showSize = false;
        } else if (!arg.startsWith('-')) {
            targetPath = arg;
        }
    });
    const tree = new TreeGenerator(targetPath, options);
    tree.generate();
}

简单实用的Python版本

#!/usr/bin/env python3
import os
def simple_tree(startpath, indent=0):
    """简单版本的树形目录显示"""
    for item in os.listdir(startpath):
        # 跳过隐藏文件
        if item.startswith('.'):
            continue
        item_path = os.path.join(startpath, item)
        prefix = '│   ' * indent + '├── '
        if os.path.isdir(item_path):
            print(f"{prefix}📁 {item}/")
            # 递归进入子目录
            simple_tree(item_path, indent + 1)
        else:
            # 获取文件大小
            size = os.path.getsize(item_path)
            if size < 1024:
                size_str = f"{size}B"
            elif size < 1024*1024:
                size_str = f"{size/1024:.1f}KB"
            else:
                size_str = f"{size/(1024*1024):.1f}MB"
            print(f"{prefix}📄 {item} ({size_str})")
if __name__ == "__main__":
    import sys
    path = sys.argv[1] if len(sys.argv) > 1 else "."
    print(f"📁 {os.path.abspath(path)}/")
    simple_tree(path)

使用示例

保存脚本后,给予执行权限:

chmod +x tree.py

运行脚本:

# 显示当前目录
./tree.py
# 显示指定目录,深度为3
./tree.py /path/to/project -d 3
# Bash脚本使用
./tree.sh /path/to/project 3
# Node.js版本
./tree.js /path/to/project --depth 3 --all

这些脚本都支持:

  • 递归显示目录结构
  • 使用Unicode字符画树形图
  • 显示目录和文件图标
  • 显示文件大小
  • 控制显示深度
  • 友好的错误处理

选择适合你需求的版本即可!

抱歉,评论功能暂时关闭!