dlc — Docker Log Cleaner(支持名称、ID前缀、交互序号清理,模糊匹配交互确认)

安装脚本:

curl -L -o /usr/local/bin/dlc https://files-cdn.cnblogs.com/files/nihaorz/dlc.sh && chmod +x /usr/local/bin/dlc && dlc -h

 

脚本源码:

#!/bin/bash
stty erase ^H 2>/dev/null
# dlc — Docker Log Cleaner(支持名称、ID前缀、交互序号清理,模糊匹配交互确认)

docker_root=$(docker info --format '{{.DockerRootDir}}' 2>/dev/null)
interactive_mode_flag=0
resolved_matches=()

# 清理指定容器日志
clean_logs() {
    local cid="$1"
    local silent="${2:-0}"
    local cname
    cname=$(docker ps -a --filter id="$cid" --format '{{.Names}}' 2>/dev/null)
    local log_dir="$docker_root/containers/$cid"
    local log_file="$log_dir/${cid}-json.log"

    if [ -d "$log_dir" ]; then
        if [ -f "$log_file" ]; then
            truncate -s 0 "$log_file" 2>/dev/null || echo -n > "$log_file"
        fi
        find "$log_dir" -type f -name "${cid}-json.log*" ! -name "${cid}-json.log" -exec rm -f {} +
        [ "$silent" -ne 1 ] && echo "✅ 容器 $cname (${cid:0:12}) 日志已清理"
    fi
}

# 解析容器标识
resolve_container_ids() {
    local input="$1"
    resolved_matches=()

    local -a ids=()
    declare -A name_map=()

    while read -r id name; do
        ids+=("$id")
        name_map["$name"]="$id"
    done < <(docker ps -a --format '{{.ID}} {{.Names}}' --no-trunc)

    if [[ "$input" =~ ^[0-9]+$ && "$interactive_mode_flag" -eq 1 ]]; then
        local sorted=($(docker ps -a --format '{{.ID}} {{.Names}}' --no-trunc | sort -k2 | awk '{print $1}'))
        local index=$((10#$input - 1))
        if [ "${sorted[$index]}" ]; then
            resolved_matches+=("${sorted[$index]}")
            return 0
        else
            return 1
        fi
    fi

    if [ "${name_map[$input]}" ]; then
        resolved_matches+=("${name_map[$input]}")
        return 0
    fi

    for id in "${ids[@]}"; do
        [[ "$id" == "$input"* ]] && resolved_matches+=("$id")
    done

    [ ${#resolved_matches[@]} -gt 0 ] && return 0 || return 1
}

# 批量清理容器日志
clean_multiple() {
    declare -A cleaned=()
    for arg in "$@"; do
        resolved_matches=()
        resolve_container_ids "$arg"
        status=$?

        if [ $status -ne 0 ]; then
            echo "❌ 未找到容器标识 '$arg'" >&2
            continue
        fi

        for cid in "${resolved_matches[@]}"; do
            [ -z "${cleaned[$cid]}" ] && cleaned[$cid]=1 && clean_logs "$cid"
        done
    done

    [ ${#cleaned[@]} -gt 0 ] && echo "🧹 所有指定容器日志已清理"
}

# 交互选择容器
interactive_mode() {
    interactive_mode_flag=1
    echo "📋 当前容器列表(按名称升序):"
    printf "%-4s | %-14s | %s\n" "序号" "容器ID" "容器名称"
    printf -- "------------------------------\n"

    local -a sorted_ids=()
    while read -r id name; do
        sorted_ids+=("$id")
        printf "%-4d | %-12s | %s\n" "${#sorted_ids[@]}" "${id:0:12}" "$name"
    done < <(docker ps -a --format '{{.ID}} {{.Names}}' --no-trunc | sort -k2)

    echo -n "请输入要清理的 序号/名称/ID前缀(空格分隔多个): "
    read -r -a inputs
    [ ${#inputs[@]} -eq 0 ] && echo "⚠️ 未输入任何标识" && exit 1

    declare -a resolved=()
    for input in "${inputs[@]}"; do
        if [[ "$input" =~ ^[0-9]+$ ]]; then
            index=$((10#$input - 1))
            [ "${sorted_ids[$index]}" ] && resolved+=("${sorted_ids[$index]}") || echo "❌ 无效序号 '$input'" >&2
        else
            resolved+=("$input")
        fi
    done

    declare -A preview=()
    for arg in "${resolved[@]}"; do
        resolved_matches=()
        resolve_container_ids "$arg"
        for cid in "${resolved_matches[@]}"; do
            [ -z "${preview[$cid]}" ] && preview[$cid]=1
        done
    done

    if [ ${#preview[@]} -eq 0 ]; then
        echo "⚠️ 未匹配到任何容器" && exit 1
    fi

    echo "🧾 即将清理以下容器日志:"
    for cid in "${!preview[@]}"; do
        cname=$(docker ps -a --filter id="$cid" --format '{{.Names}}')
        echo "  - $cname (${cid:0:12})"
    done

    echo -n "⚠️ 是否确认清理这些容器日志?(y/N): "
    read -r -n1 confirm && echo
    [[ "$confirm" =~ ^[Yy]$ ]] || { echo "❌ 已取消"; exit 0; }

    clean_multiple "${resolved[@]}"
}

# 清理所有容器日志
clean_all() {
    local force="${1:-0}"
    local -a all_ids=($(docker ps -a --no-trunc -q))
    [ ${#all_ids[@]} -eq 0 ] && echo "📭 没有容器可清理" && exit 0

    if [ "$force" -ne 1 ]; then
        echo -n "⚠️ 即将清理 ${#all_ids[@]} 个容器日志,确认?(y/N): "
        read -r -n1 confirm && echo
        [[ "$confirm" =~ ^[Yy]$ ]] || { echo "❌ 已取消"; exit 0; }
    fi

    for cid in "${all_ids[@]}"; do clean_logs "$cid" 1; done
    echo "✅ 所有容器日志已清理"
}

# 帮助信息
show_help() {
    cat <<EOF

dlc — Docker Log Cleaner(支持名称、ID前缀、交互序号清理,模糊匹配交互确认)

用法: dlc [选项] [容器标识]...

选项:
  -l, --list         交互选择容器(支持多个)
  -a, --all          清理所有容器日志(默认需确认)
  -f, --force        跳过确认直接清理(需与 -a 组合使用)
  -h, --help         显示帮助信息

组合参数支持:
  -af, -fa           等同于 -a -f,清理所有容器日志并跳过确认

容器标识支持:
  - 名称(如 nginx)
  - ID前缀(如 fc82)
  - 序号(仅在交互模式下,如 1 2 3)

示例:
  dlc nginx fc82             # 按名称或 ID 前缀清理
  dlc -l                     # 交互选择容器
  dlc -a                     # 清理所有容器日志(需确认)
  dlc -af                    # 清理所有容器日志(跳过确认)

清理规则说明:
  - 主日志文件(container-id-json.log)将被清空内容,文件保留
  - 滚动日志文件(如 container-id-json.log.1、.log.2.gz 等)将被直接删除
  - 不影响容器运行状态或其他数据
  - 若日志文件不存在则跳过,不报错
  - 支持模糊匹配(ID前缀、名称),交互模式下支持序号选择
  
EOF
}

# 主入口
main() {
    local force=0
    local all=0
    local list=0
    local help=0
    local args=()
    local positional=()

    for raw in "$@"; do
        if [[ "$raw" =~ ^-([a-zA-Z]{2,})$ ]]; then
            for ((i=1; i<${#raw}; i++)); do
                args+=("-${raw:$i:1}")
            done
        else
            args+=("$raw")
        fi
    done

    for arg in "${args[@]}"; do
        case "$arg" in
            -f|--force) force=1 ;;
            -a|--all)   all=1 ;;
            -l|--list)  list=1 ;;
            -h|--help)  help=1 ;;
        *)          positional+=("$arg") ;;
    esac
done

if [ "$help" -eq 1 ] || [ "$all$list" == "00" ] && [ ${#positional[@]} -eq 0 ]; then
    show_help
elif [ "$list" -eq 1 ]; then
    interactive_mode
elif [ "$all" -eq 1 ]; then
    clean_all "$force"
else
    clean_multiple "${positional[@]}"
fi
}

main "$@"

 

posted @ 2025-10-20 12:22  Nihaorz  阅读(4)  评论(0)    收藏  举报