Files
script/system_manager.sh

482 lines
15 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# Linux 全能系统管理工具 (Pro版)
# 支持: Debian系 / RHEL系(含麒麟,龙蜥,欧拉) / Arch / Alpine / OpenSUSE
# 功能: Docker管理 | Swap管理 | 工具箱 | 系统源优化
# 版本: 2.0.0
# ==============================================================================
# ---------- 1. 全局配置与初始化 ----------
# 严格模式 (部分开启,避免过早退出)
set -o pipefail
# 路径配置
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG_FILE="${SCRIPT_DIR}/sys_manager.log"
readonly SWAP_FILE="/swapfile"
readonly DOCKER_CONFIG="/etc/docker/daemon.json"
# 颜色定义
readonly C_RED='\033[0;31m'
readonly C_GREEN='\033[0;32m'
readonly C_YELLOW='\033[1;33m'
readonly C_BLUE='\033[0;34m'
readonly C_CYAN='\033[0;36m'
readonly C_RESET='\033[0m'
# 全局变量 (由 sys_detect 填充)
G_OS_DISTRO="" # 发行版 ID (ubuntu, centos, alpine, kylin...)
G_PKG_MANAGER="" # 包管理器 (apt, dnf, yum, apk, pacman...)
G_SVC_MANAGER="" # 服务管理器 (systemd, openrc, service)
G_IS_CHINA="" # 是否在中国大陆
# ---------- 2. 基础工具函数 ----------
log() {
local level="$1"
local msg="$2"
local color=""
case "$level" in
INFO) color="$C_BLUE" ;;
SUCCESS) color="$C_GREEN" ;;
WARN) color="$C_YELLOW" ;;
ERROR) color="$C_RED" ;;
esac
echo -e "${color}[$(date +'%H:%M:%S')][${level}]${C_RESET} ${msg}" | tee -a "$LOG_FILE"
}
# 装饰性标题
print_banner() {
local title="$1"
echo
echo -e "${C_CYAN}══════════════════════════════════════════════════════${C_RESET}"
echo -e "${C_CYAN} $title${C_RESET}"
echo -e "${C_CYAN}══════════════════════════════════════════════════════${C_RESET}"
echo
}
# 交互确认
confirm() {
local prompt="${1:-确认执行此操作?}"
local default="${2:-n}" # 默认 no
local choice
echo -ne "${C_YELLOW}${prompt} [y/N]: ${C_RESET}"
read -r choice
[[ "$choice" =~ ^[Yy]$ ]]
}
pause() {
echo
read -rp "按回车键继续..." -s
echo
}
# ---------- 3. 系统抽象层 (核心) ----------
sys_detect() {
# 1. 权限检查
[[ $EUID -ne 0 ]] && { log ERROR "请使用 sudo 或 root 运行此脚本"; exit 1; }
# 2. 发行版检测
if [[ -f /etc/os-release ]]; then
source /etc/os-release
G_OS_DISTRO="${ID,,}"
else
G_OS_DISTRO="unknown"
fi
# 3. 包管理器检测
if command -v dnf &>/dev/null; then G_PKG_MANAGER="dnf"
elif command -v apt &>/dev/null; then G_PKG_MANAGER="apt"
elif command -v yum &>/dev/null; then G_PKG_MANAGER="yum"
elif command -v apk &>/dev/null; then G_PKG_MANAGER="apk"
elif command -v pacman &>/dev/null; then G_PKG_MANAGER="pacman"
elif command -v zypper &>/dev/null; then G_PKG_MANAGER="zypper"
else
log ERROR "无法识别包管理器,不支持此系统"
exit 1
fi
# 4. 服务管理器检测
if command -v systemctl &>/dev/null; then G_SVC_MANAGER="systemd"
elif command -v rc-service &>/dev/null; then G_SVC_MANAGER="openrc" # Alpine
else G_SVC_MANAGER="service"; fi
# 5. 区域检测 (用于镜像源优化)
if curl -s --connect-timeout 2 -m 2 https://www.google.com &>/dev/null; then
G_IS_CHINA="false"
else
G_IS_CHINA="true"
fi
}
# 统一包安装接口
sys_install_pkg() {
local pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && return 0
log INFO "正在安装: ${pkgs[*]} (使用 $G_PKG_MANAGER)..."
case "$G_PKG_MANAGER" in
apt) apt-get update -qq && apt-get install -y "${pkgs[@]}" ;;
dnf|yum) $G_PKG_MANAGER install -y "${pkgs[@]}" ;;
apk) apk add --no-cache "${pkgs[@]}" ;;
pacman) pacman -Sy --noconfirm "${pkgs[@]}" ;;
zypper) zypper install -y --no-confirm "${pkgs[@]}" ;;
esac
}
# 统一包卸载接口
sys_remove_pkg() {
local pkgs=("$@")
[[ ${#pkgs[@]} -eq 0 ]] && return 0
log INFO "正在卸载: ${pkgs[*]}..."
case "$G_PKG_MANAGER" in
apt) apt-get purge -y "${pkgs[@]}" && apt-get autoremove -y ;;
dnf|yum) $G_PKG_MANAGER remove -y "${pkgs[@]}" ;;
apk) apk del "${pkgs[@]}" ;;
pacman) pacman -Rs --noconfirm "${pkgs[@]}" ;;
zypper) zypper remove -y "${pkgs[@]}" ;;
esac
}
# 统一服务管理接口
sys_service() {
local action="$1" # start, stop, restart, enable
local service="$2"
[[ -z "$service" ]] && return 1
case "$G_SVC_MANAGER" in
systemd)
[[ "$action" == "enable" ]] && systemctl enable --now "$service" || systemctl "$action" "$service"
;;
openrc)
[[ "$action" == "enable" ]] && rc-update add "$service" default && rc-service "$service" start || rc-service "$service" "$action"
;;
*)
service "$service" "$action"
;;
esac
}
# ---------- 4. Docker 模块 ----------
mod_docker_menu() {
while true; do
clear
print_banner "Docker 容器管理"
local status="未安装"
command -v docker &>/dev/null && status="${C_GREEN}已安装 $(docker --version | awk '{print $3}' | tr -d ,)${C_RESET}"
echo -e "当前状态: $status"
echo "1. 安装 Docker (自动适配源)"
echo "2. 配置国内镜像加速 (智能检测)"
echo "3. 卸载 Docker"
echo "0. 返回上级"
echo
read -rp "请选择: " opt
case $opt in
1) mod_docker_install ;;
2) mod_docker_mirror ;;
3) mod_docker_remove ;;
0) return ;;
*) log ERROR "无效选择" ;;
esac
pause
done
}
mod_docker_install() {
if command -v docker &>/dev/null; then
log WARN "Docker 已安装"
return
fi
log INFO "开始安装 Docker..."
# 特殊处理 Alpine
if [[ "$G_OS_DISTRO" == "alpine" ]]; then
sys_install_pkg docker docker-compose
sys_service enable docker
log SUCCESS "Docker 安装完成 (Alpine)"
return
fi
# 通用官方脚本安装 (兼容性最好)
local install_cmd="curl -fsSL https://get.docker.com | bash"
if [[ "$G_IS_CHINA" == "true" ]]; then
install_cmd="curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun"
fi
eval "$install_cmd"
if command -v docker &>/dev/null; then
sys_service enable docker
log SUCCESS "Docker 安装成功"
else
log ERROR "Docker 安装失败,请检查网络或系统日志"
fi
}
mod_docker_mirror() {
if ! command -v docker &>/dev/null; then log ERROR "请先安装 Docker"; return; fi
[[ "$G_IS_CHINA" != "true" ]] && { log INFO "检测到非中国大陆IP通常无需配置加速"; confirm "仍要配置吗?" || return; }
local mirrors='["https://docker.1panel.live", "https://docker.m.daocloud.io"]'
mkdir -p "$(dirname "$DOCKER_CONFIG")"
cat > "$DOCKER_CONFIG" <<EOF
{
"registry-mirrors": $mirrors,
"log-driver": "json-file",
"log-opts": {"max-size": "50m", "max-file": "3"}
}
EOF
sys_service restart docker
log SUCCESS "镜像加速配置完成"
}
mod_docker_remove() {
confirm "确定彻底卸载 Docker 及其数据吗?" || return
sys_service stop docker
# 根据包管理器卸载
local pkgs=(docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker docker-engine docker.io containerd runc)
sys_remove_pkg "${pkgs[@]}"
rm -rf /var/lib/docker /var/lib/containerd /etc/docker
log SUCCESS "Docker 已卸载并清理数据"
}
# ---------- 5. Swap 交换分区模块 ----------
mod_swap_menu() {
while true; do
clear
print_banner "Swap 虚拟内存管理"
# 获取当前 Swap 信息
local current_swap
current_swap=$(free -m | awk '/Swap/ {print $2}')
local swap_status="${C_RED}未启用${C_RESET}"
[[ "$current_swap" -gt 0 ]] && swap_status="${C_GREEN}已启用 (${current_swap} MB)${C_RESET}"
echo -e "当前状态: $swap_status"
echo "1. 创建/扩容 Swap"
echo "2. 删除 Swap"
echo "0. 返回上级"
echo
read -rp "请选择: " opt
case $opt in
1) mod_swap_create ;;
2) mod_swap_remove ;;
0) return ;;
esac
pause
done
}
mod_swap_create() {
# 检查虚拟化限制
if [[ -d "/proc/vz" && ! -d "/proc/bc" ]]; then
log ERROR "OpenVZ 架构通常不支持 Swap"
return
fi
if grep -q "$SWAP_FILE" /etc/fstab; then
log WARN "发现已存在的 Swap 配置,请先删除"
return
fi
local mem_total
mem_total=$(free -m | awk '/Mem:/ {print $2}')
local recommend=$((mem_total < 2048 ? mem_total * 2 : mem_total))
echo -e "物理内存: ${mem_total} MB"
read -rp "请输入 Swap 大小 (MB) [默认 ${recommend}]: " size
size=${size:-$recommend}
log INFO "正在创建 ${size} MB 的 Swap 文件..."
# 兼容性创建 (dd 比 fallocate 兼容性好,尤其是 btrfs/xfs 某些情况)
dd if=/dev/zero of="$SWAP_FILE" bs=1M count="$size" status=progress
chmod 600 "$SWAP_FILE"
mkswap "$SWAP_FILE"
swapon "$SWAP_FILE"
# 持久化
echo "$SWAP_FILE none swap sw 0 0" >> /etc/fstab
log SUCCESS "Swap 创建成功"
}
mod_swap_remove() {
confirm "确定删除 Swap 吗?" || return
swapoff "$SWAP_FILE" 2>/dev/null
sed -i "\|$SWAP_FILE|d" /etc/fstab
rm -f "$SWAP_FILE"
log SUCCESS "Swap 已移除"
}
# ---------- 6. 常用工具模块 ----------
# 定义工具字典 (格式: "工具名:apt包名:yum包名:apk包名:pacman包名:zypper包名")
# 如果包名相同,后续可以省略,脚本会自动回退使用默认名
# 例如: "vim" 在所有系统都叫 vim除了 alpine 叫 vim (这里只是举例逻辑)
declare -A TOOLS_MAP=(
["vim"]="vim:vim:vim:vim:vim:vim"
["wget"]="wget"
["curl"]="curl"
["git"]="git"
["htop"]="htop"
["tmux"]="tmux"
["zsh"]="zsh"
["unzip"]="unzip"
["net-tools"]="net-tools:net-tools:net-tools:net-tools:net-tools:net-tools" # ifconfig
["ping"]="iputils-ping:iputils:iputils:iputils-ping:iputils:iputils" # ping 命令包名差异大
)
mod_tools_get_pkg_name() {
local tool=$1
local def="${TOOLS_MAP[$tool]}"
local pkg_name=""
IFS=':' read -r -a parts <<< "$def"
# 根据全局 G_PKG_MANAGER 映射索引
# 0:通用/apt, 1:yum/dnf, 2:apk, 3:pacman, 4:zypper
local idx=0
case "$G_PKG_MANAGER" in
apt) idx=1 ;;
yum|dnf) idx=2 ;;
apk) idx=3 ;;
pacman) idx=4 ;;
zypper) idx=5 ;;
esac
# 获取对应位置的包名,如果没有则使用第一位(通用名)
if [[ -n "${parts[$idx]}" ]]; then
echo "${parts[$idx]}"
else
echo "${parts[0]}" # Fallback
fi
}
mod_tools_menu() {
while true; do
clear
print_banner "常用工具箱 (适配 $G_PKG_MANAGER)"
local i=1
local tool_list=()
for tool in "${!TOOLS_MAP[@]}"; do
local status="${C_RED}未装${C_RESET}"
# 简单的检查命令是否存在,部分包名和命令名不一致的需特殊处理,这里简化处理
if command -v "$tool" &>/dev/null || ([[ "$tool" == "ping" ]] && command -v ping &>/dev/null); then
status="${C_GREEN}已装${C_RESET}"
fi
printf "%2d. %-12s [%s]\n" "$i" "$tool" "$status"
tool_list+=("$tool")
((i++))
done
echo
echo " a. 一键安装所有"
echo " 0. 返回上级"
echo
read -rp "请输入序号安装 (支持多选,空格分隔,如 1 3): " choices
case "$choices" in
0) return ;;
a|A)
local all_pkgs=()
for t in "${tool_list[@]}"; do
all_pkgs+=("$(mod_tools_get_pkg_name "$t")")
done
sys_install_pkg "${all_pkgs[@]}"
pause
;;
*)
local install_list=()
for c in $choices; do
if [[ "$c" =~ ^[0-9]+$ ]] && [ "$c" -le "${#tool_list[@]}" ] && [ "$c" -ge 1 ]; then
local t_name="${tool_list[$((c-1))]}"
install_list+=("$(mod_tools_get_pkg_name "$t_name")")
fi
done
if [[ ${#install_list[@]} -gt 0 ]]; then
sys_install_pkg "${install_list[@]}"
else
log ERROR "无效的选择"
fi
pause
;;
esac
done
}
# ---------- 7. 系统源更换模块 ----------
mod_mirrors_run() {
print_banner "系统软件源更换"
log INFO "正在调用 LinuxMirrors 开源脚本..."
log WARN "此操作将替换系统源配置文件,请确保网络通畅。"
if confirm "继续执行?"; then
# 确保有 curl
if ! command -v curl &>/dev/null; then
sys_install_pkg curl
fi
# 调用成熟的外部脚本,避免重复造轮子
bash <(curl -sSL https://linuxmirrors.cn/main.sh)
fi
}
# ---------- 8. 主程序循环 ----------
main() {
sys_detect # 系统环境初始化
# 捕获 Ctrl+C
trap 'echo -e "\n${C_YELLOW}用户取消操作,正在退出...${C_RESET}"; exit 0' SIGINT
while true; do
clear
print_banner "Linux System Manager Pro"
echo -e " 系统信息: ${C_GREEN}$G_OS_DISTRO${C_RESET} | 包管理: ${C_GREEN}$G_PKG_MANAGER${C_RESET} | 区域: $([[ "$G_IS_CHINA" == "true" ]] && echo "CN" || echo "Global")"
echo -e " 核心时间: $(uptime -p)"
echo
echo " 1. Docker 管理中心 (容器/镜像/加速)"
echo " 2. Swap 虚拟内存 (创建/扩容/删除)"
echo " 3. 常用工具箱 (Vim/Git/Tmux...)"
echo " 4. 系统源一键更换 (调用 LinuxMirrors)"
echo " 5. 查看系统日志 (查看脚本运行记录)"
echo " 0. 退出程序"
echo
read -rp " 请输入数字 [0-5]: " choice
case "$choice" in
1) mod_docker_menu ;;
2) mod_swap_menu ;;
3) mod_tools_menu ;;
4) mod_mirrors_run ;;
5)
echo
tail -n 20 "$LOG_FILE"
pause
;;
0)
log INFO "退出程序"
exit 0
;;
*) log ERROR "输入错误" ; sleep 1 ;;
esac
done
}
main