系统优化脚本
- centos7
- rocky10
- ubuntu
#!/usr/bin/env bash
#
set -e
# 修改系统yum源为aliyun并添加epel源
# 幂等:首次运行才备份原 repo 并下载阿里 repo,之后跳过(重复执行安全)
# 注意:条件判断用 if 而非 [ ] &&,否则条件不满足时 set -e 会中断脚本
if [ ! -e /etc/yum.repos.d/CentOS-Base.repo ]; then
[ -e /etc/yum.repos.d/CentOS-Base.repo.bak ] || mv /etc/yum.repos.d/CentOS-Base.repo{,.bak} 2>/dev/null || true
curl -fsSL -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
fi
sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
if [ ! -e /etc/yum.repos.d/epel.repo ]; then
curl -fsSL -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
fi
yum makecache
yum -y install ntpdate tar wget net-tools git vim tree lrzsz htop iftop iotop psmisc python36 python3-devel zlib zlib-devel gcc gcc-c++ conntrack-tools jq socat bash-completion telnet nload strace tcpdump lsof sysstat
# 关闭swap
# 幂等:跳过已注释的行,避免重复执行时加多层 #
sed -i '/^\s*#/!{ /swap/s/^/#/ }' /etc/fstab
# 关闭防火墙、selinux、NetworkManager
systemctl disable --now firewalld NetworkManager
# 幂等:按 SELINUX= 前缀匹配(原来写 死第 7 行,行号变了就失效)
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 同步时间计划任务
# 幂等:已存在则不追加
grep -q 'ntpdate ntp2.aliyun.com' /var/spool/cron/root 2>/dev/null || \
echo "*/10 * * * * /usr/sbin/ntpdate ntp2.aliyun.com &>/dev/null" >> /var/spool/cron/root
# 历史命令显示时间
sed -i '/HISTFILESIZE=2000/d' /etc/bashrc
sed -i '/HISTSIZE=2000/d' /etc/bashrc
sed -i '/HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "/d' /etc/bashrc
sed -i '/export HISTTIMEFORMAT/d' /etc/bashrc
cat >> /etc/bashrc << EOF
HISTFILESIZE=2000
HISTSIZE=2000
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
export HISTTIMEFORMAT
EOF
source /etc/bashrc
# 修改系统最大文件描述符
sed -i '/root soft nofile 65535/d' /etc/security/limits.conf
sed -i '/root hard nofile 65535/d' /etc/security/limits.conf
sed -i '/* soft nofile 65535/d' /etc/security/limits.conf
sed -i '/* hard nofile 65535/d' /etc/security/limits.conf
cat >>/etc/security/limits.conf<<'EOF'
root soft nofile 65535
root hard nofile 65535
* soft nofile 65535
* hard nofile 65535
EOF
# 设置pip国内源
[ -d ~/.pip ] || mkdir ~/.pip
cat > ~/.pip/pip.conf << EOF
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
EOF
# 安装 glances mycli
pip3 install --upgrade pip
pip3 install glances mycli==1.24.0
# ===== 安装并配置 grc(源码安装:clone 官方仓库 + 官方 install.sh;命令输出着色)=====
# 安装路径与官方一致:bin→/usr/bin、conf→/usr/share/grc、grc.conf→/etc、
# grc.sh→/etc/profile.d/、grc.zsh→/etc
# 幂等:已安装则跳过;clone 失败仅提示跳过,不中断脚本(配置段探测不到文件就不会写入)
if ! command -v grc >/dev/null 2>&1; then
rm -rf /tmp/grc-src
# clone 和安装一条链:任一步失败都走 else 分支提示跳过,不会中断脚本
if git clone --depth 1 https://github.com/garabik/grc.git /tmp/grc-src \
&& (cd /tmp/grc-src && ./install.sh /usr ""); then
echo "grc: 源码安装完成(/usr/bin/grc,配置在 /etc/profile.d/grc.sh、/etc/grc.zsh)"
else
echo "grc: git clone 或 install.sh 失败(网络原因?),跳过安装,着色配置不生效"
fi
fi
# 配置部分:依据官方 README/源码,bash 的 grc.sh 必须先设 GRC_ALIASES=true 才会创建别名(grc.zsh 无此要求);
# bash 启用文件为 /etc/profile.d/grc.sh(源码与 Debian 包默认位置),zsh 为 /etc/grc.zsh;已配置则跳过
USER_SHELL="$(getent passwd "$(id -un)" | cut -d: -f7)"
GRC_CFG=""
RCFILE="$HOME/.bashrc"
case "$USER_SHELL" in
*/zsh)
RCFILE="$HOME/.zshrc"
for f in /etc/grc.zsh /usr/local/etc/grc.zsh /usr/share/grc/grc.zsh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
*)
for f in /etc/profile.d/grc.sh /etc/grc.sh /usr/local/etc/profile.d/grc.sh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
esac
if [ -n "$GRC_CFG" ] && ! grep -q 'grc' "$RCFILE" 2>/dev/null; then
if [ "$(basename "$GRC_CFG")" = "grc.sh" ]; then
echo 'GRC_ALIASES=true' >> "$RCFILE"
fi
echo "[ -s $GRC_CFG ] && . $GRC_CFG" >> "$RCFILE"
fi
# 设置别名(幂等,避免重复追加)
grep -q "alias n=" ~/.bashrc || cat >> ~/.bashrc << EOF
alias n='netstat -ntpl'
alias u='netstat -nupl'
alias a='netstat -natupl'
EOF
# 重启系统使部分配置生效
reboot
#!/usr/bin/env bash
#
set -e
# 修改系统yum源为阿里云
# 幂等:备份仅在首次(.bak 不存在时),重复执行时 sed 无匹配、内容不变
for f in /etc/yum.repos.d/[Rr]ocky*.repo; do
[ -f "${f}.bak" ] || cp "$f" "${f}.bak"
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
-i "$f"
done
# 添加epel源
dnf install -y epel-release
# 修改epel源为阿里云
if [ -f /etc/yum.repos.d/epel.repo ]; then
[ -f /etc/yum.repos.d/epel.repo.bak ] || cp /etc/yum.repos.d/epel.repo{,.bak}
sed -e 's|^metalink=|#metalink=|g' \
-e 's|^#baseurl=https://download.example/pub/epel|baseurl=https://mirrors.aliyun.com/epel|g' \
-i /etc/yum.repos.d/epel.repo
fi
# 更新缓存
dnf makecache
# 设置24小时制
localectl set-locale LC_TIME=en_GB.UTF-8
# 安装一些常用工具
dnf -y install \
yum-utils \
tar \
wget \
net-tools \
git \
vim \
tree \
lrzsz \
htop \
iftop \
iotop \
psmisc \
python3-devel \
zlib \
zlib-devel \
gcc \
gcc-c++ \
conntrack-tools \
jq \
socat \
bash-completion \
telnet \
nload \
strace \
tcpdump \
lsof \
sysstat
# 关闭swap
# 幂等:跳过已注释的行,避免重复执行时加多层 #
sed -i '/^\s*#/!{ /swap/s/^/#/ }' /etc/fstab
swapoff -a
# Rocky 10 部分安装方式默认带 zram 交换设备(不受 fstab 控制),一并关闭
dnf remove -y zram-generator-defaults >/dev/null 2>&1 || true
# 关闭防火墙、selinux
systemctl disable --now firewalld
sed -i.bak 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
# 配置时间同步(Rocky 10 用 chrony 替代 ntpdate,改为国内 NTP 源)
dnf install -y chrony
# 幂等:先删掉默认 pool 和之前追加过的 server 行,再追加,避免重复堆叠
sed -i '/^pool/d; /^server ntp.aliyun.com/d; /^server ntp.tencent.com/d; /^server cn.pool.ntp.org/d' /etc/chrony.conf
cat >> /etc/chrony.conf << 'EOF'
server ntp.aliyun.com iburst
server ntp.tencent.com iburst
server cn.pool.ntp.org iburst
EOF
# 时钟偏差超 1 秒就立即跳变校准,不限测量次数(默认 makestep 1.0 3 只在
# 启动后前 3 次测量生效;-1 兜底虚拟机挂起恢复等运行期大偏差场景)
# 幂等:任意 makestep 值都替换为 1.0 -1,不存在时补写
sed -i 's/^makestep .*/makestep 1.0 -1/' /etc/chrony.conf
grep -q '^makestep' /etc/chrony.conf || echo 'makestep 1.0 -1' >> /etc/chrony.conf
grep -n '^makestep' /etc/chrony.conf
systemctl enable --now chronyd
systemctl restart chronyd
# 定时兜底:每 10 分钟强制步进校准一次(chronyd 常驻本身已持续自动同步,
# 此条是虚拟机时钟漂移等场景的保险;root 走本地 unix socket 无需认证——
# 老写法 chronyc -a makestep 带 -a 要密码会报 501 Not authorised ,不能用)
# 幂等:cron 里已有该任务则不重复追加
grep -q 'chronyc makestep' /var/spool/cron/root 2>/dev/null || \
echo "*/10 * * * * /usr/bin/chronyc makestep &>/dev/null" >> /var/spool/cron/root
# 仿 ntpdate:一键强制同步时间(用法:ntpdate [NTP服务器],如 ntpdate ntp1.aliyun.com)
cat > /usr/local/bin/ntpdate <<'EOF'
#!/bin/bash
# 模拟 ntpdate:强制同步一次时间(兼容原用法,可选指定 NTP 服务器)
SERVER="${1:-ntp.aliyun.com}"
systemctl stop chronyd
chronyd -q "server ${SERVER} iburst"
systemctl start chronyd
EOF
chmod +x /usr/local/bin/ntpdate
# 配置历史命令显示时间
sed -i '/HISTFILESIZE=2000/d; /HISTSIZE=2000/d; /HISTTIMEFORMAT="/d; /export HISTTIMEFORMAT/d' /etc/bashrc
cat >> /etc/bashrc << 'EOF'
HISTFILESIZE=2000
HISTSIZE=2000
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
export HISTTIMEFORMAT
EOF
source /etc/bashrc
# 修改系统最大文件描述符
sed -i '/root soft nofile 65535/d; /root hard nofile 65535/d; /\* soft nofile 65535/d; /\* hard nofile 65535/d' /etc/security/limits.conf
cat >> /etc/security/limits.conf << 'EOF'
root soft nofile 65535
root hard nofile 65535
* soft nofile 65535
* hard nofile 65535
EOF
# 设置pip国内源
[ -d ~/.pip ] || mkdir ~/.pip
cat > ~/.pip/pip.conf << EOF
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
EOF
# 安装 glances mycli
pip3 install --upgrade pip
pip3 install glances mycli
# ===== 安装并配置 grc(源码安装:clone 官方仓库 + 官方 install.sh;命令输出着色)=====
# 安装路径与官方一致:bin→/usr/bin、conf→/usr/share/grc、grc.conf→/etc、
# grc.sh→/etc/profile.d/、grc.zsh→/etc
# 幂等:已安装则跳过;clone 失败仅提示跳过,不中断脚本(配置段探测不到文件就不会写入)
if ! command -v grc >/dev/null 2>&1; then
rm -rf /tmp/grc-src
# clone 和安装一条链:任一步失败都走 else 分支提示跳过,不会中断脚本
if git clone --depth 1 https://github.com/garabik/grc.git /tmp/grc-src \
&& (cd /tmp/grc-src && ./install.sh /usr ""); then
echo "grc: 源码安装完成(/usr/bin/grc,配置在 /etc/profile.d/grc.sh、/etc/grc.zsh)"
else
echo "grc: git clone 或 install.sh 失败(网络原因?),跳过安装,着色配置不生效"
fi
fi
# 配置部分:依据官方 README/源码,bash 的 grc.sh 必须先设 GRC_ALIASES=true 才会创建别名(grc.zsh 无此要求);
# bash 启用文件为 /etc/profile.d/grc.sh(源码与 Debian 包默认位置),zsh 为 /etc/grc.zsh;已配置则跳过
USER_SHELL="$(getent passwd "$(id -un)" | cut -d: -f7)"
GRC_CFG=""
RCFILE="$HOME/.bashrc"
case "$USER_SHELL" in
*/zsh)
RCFILE="$HOME/.zshrc"
for f in /etc/grc.zsh /usr/local/etc/grc.zsh /usr/share/grc/grc.zsh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
*)
for f in /etc/profile.d/grc.sh /etc/grc.sh /usr/local/etc/profile.d/grc.sh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
esac
if [ -n "$GRC_CFG" ] && ! grep -q 'grc' "$RCFILE" 2>/dev/null; then
if [ "$(basename "$GRC_CFG")" = "grc.sh" ]; then
echo 'GRC_ALIASES=true' >> "$RCFILE"
fi
echo "[ -s $GRC_CFG ] && . $GRC_CFG" >> "$RCFILE"
fi
# 设置别名(幂等,避免重复追加)
grep -q "alias n=" ~/.bashrc || cat >> ~/.bashrc << EOF
alias n='netstat -ntpl'
alias u='netstat -nupl'
alias a='netstat -natupl'
EOF
# 重启系统使部分配置生效
reboot
rocky10 与 centos7 版的差异说明
Rocky 10 已移除 ntpdate,用 chrony 替代:常驻的 chronyd 本身就持续自动同步(无需像 ntp 时代那样靠 cron 拉起一次性校时)。时间校准共三层:① makestep 1.0 -1 让偏差超 1 秒时由 chronyd 自动跳变(含挂起恢复场景);② cron 每 10 分钟执行 chronyc makestep 强制步进兜底(root 走本地 unix socket 无需认证;老写法 chronyc -a makestep 带 -a 要密码会报 501 Not authorised,不能用);③ 真发现大偏差时用 ntpdate 命令(内部执行 chronyd -q)手动强制跳变一次。
22.04 / 24.04 / 26.04 最小化安装的差异与结论
需要做的(脚本已全部幂等处理,三个版本通用):换 apt 阿里源、关 swap(Server 最小化安装默认有 /swap.img)、关 ufw、装 chrony 配国内 NTP + makestep 1.0 -1、装 net-tools(最小化安装不带 netstat)、ulimits、pip 源。
不需要做的(与 Rocky 的关键差异):
- 无 epel:Ubuntu 没有 EPEL 概念,常用工具都在官方源里
- 无 SELinux:Ubuntu 用 AppArmor,默认策略不影响运维操作,不建议关闭
- 不动 NetworkManager:Server 版网络栈是 netplan + systemd-networkd,禁用 NM 反而可能断网
三版本差异点:22.04 用传统单行格式 /etc/apt/sources.list,24.04/26.04 改为 deb822 格式的 /etc/apt/sources.list.d/ubuntu.sources(脚本两个文件都处理);24.04/26.04 启用 PEP668 管控系统级 pip 装包(脚本自动加 --break-system-packages);默认 swap 大小不同但处理方式相同。
#!/usr/bin/env bash
#
# Ubuntu 22.04 / 24.04 / 26.04 最小化安装(Server)优化脚本
# 幂等设计:重复执行安全;版本差异自动适配(见各段注释)
# 双模式设计:
# root(或 sudo)执行 → 系统级优化 + 用户级配置写给登录用户(SUDO_USER)
# 普通用户直接执行 → 只做用户级优化(自己的 bashrc/pip 源/别名),系统级部分跳过
set -e
# ===== 兼容守卫:用 sh 执行时自动切换到 bash =====
# Ubuntu 的 sh 是 dash(无 source、不支持 bash 特性),sh yh.sh 会忽略 shebang 强制用 dash。
# 这段让 dash 启动后立即 exec 到 bash 重新运行,sh/bash 执行效果完全一致
if [ -z "${BASH_VERSION:-}" ]; then
exec bash "$0" "$@"
fi
# ===== 0. 版本检测(22.04 / 24.04 / 26.04)=====
. /etc/os-release
UBU_VER="${VERSION_ID}"
echo "当前 Ubuntu 版本: ${UBU_VER} (${VERSION_CODENAME:-})"
# ===== 用户级配置的目标用户 =====
# 原则:系统级操作(apt/swap/systemctl)需要 root,但用户级配置(bashrc/pip 源/别名)
# 应写给"真正登录的用户"而非 root——sudo 执行时 $HOME 指向 /root,$SUDO_USER 才是登录用户
# 例:pptfz 登录后执行 sudo sh yh.sh → 配置写入 /home/pptfz;root 直接执行 → 写入 /root
TARGET_USER="${SUDO_USER:-$USER}"
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
echo "用户级配置目标: ${TARGET_USER} (${TARGET_HOME})"
# ===== root 判定:系统级操作仅 root 可做 =====
if [ "$(id -u)" -eq 0 ]; then
IS_ROOT=1
echo "运行模式: root —— 系统级 + 用户级 全部执行"
else
IS_ROOT=0
echo "运行模式: 普通用户 —— 仅执行用户级配置,系统级部分跳过(补全请用 sudo 执行)"
fi
if [ "$IS_ROOT" -eq 1 ]; then
# ============================================================
# 第一部分:系统级优化(需要 root)
# ============================================================
# ===== 1. 换 apt 源为阿里云 =====
# 版本差异:22.04 用传统单行格式 /etc/apt/sources.list;
# 24.04/26.04 用 deb822 格式 /etc/apt/sources.list.d/ubuntu.sources
# 两个文件都做替换,存在哪个换哪个
# 注意匹配模式必须带 /ubuntu 路径:只替换主机名的话,原路径里的 /ubuntu 会被保留,
# 拼出 mirrors.aliyun.com/ubuntu/ubuntu 双层路径导致 404
# 幂等:备份只在第一次做(.bak 不存在才 cp),且已配置过阿里源则整体跳过
if [ -f /etc/apt/sources.list ] && ! grep -q 'mirrors.aliyun.com' /etc/apt/sources.list; then
[ -f /etc/apt/sources.list.bak ] || cp /etc/apt/sources.list{,.bak}
sed -i 's|http://cn.archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g;
s|http://archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g;
s|http://security.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list
fi
if [ -f /etc/apt/sources.list.d/ubuntu.sources ] && ! grep -q 'mirrors.aliyun.com' /etc/apt/sources.list.d/ubuntu.sources; then
[ -f /etc/apt/sources.list.d/ubuntu.sources.bak ] || cp /etc/apt/sources.list.d/ubuntu.sources{,.bak}
sed -i 's|http://cn.archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g;
s|http://archive.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g;
s|http://security.ubuntu.com/ubuntu|https://mirrors.aliyun.com/ubuntu|g' /etc/apt/sources.list.d/ubuntu.sources
fi
apt-get update
# ===== 2. 安装常用工具与 chrony =====
# net-tools 最小化安装不自带(netstat 命令依赖它);
# 安装 chrony 时会自动停用 systemd-timesyncd(Ubuntu 默认用它同步时间),二者不共存
apt-get install -y net-tools curl wget vim lsof bash-completion chrony git
# ===== 3. 时区与时间同步(chrony,与 Rocky 部分同一套逻辑)=====
timedatectl set-timezone Asia/Shanghai
CONF=/etc/chrony/chrony.conf # 注意:Ubuntu 的 chrony 配置路径与 Rocky 不同
# 幂等:先删掉默认 pool 和之前追加过的 server 行,再追加,避免重复堆叠
sed -i '/^pool /d; /^server ntp.aliyun.com/d; /^server ntp.tencent.com/d' ${CONF}
cat >> ${CONF} << 'EOF'
server ntp.aliyun.com iburst
server ntp.tencent.com iburst
EOF
# 与 Rocky 部分相同:偏差超 1 秒随时跳变(默认 makestep 1 3 只管启动后前 3 次)
sed -i 's/^makestep .*/makestep 1.0 -1/' ${CONF}
grep -q '^makestep' ${CONF} || echo 'makestep 1.0 -1' >> ${CONF}
grep -n '^makestep' ${CONF}
systemctl enable --now chrony
systemctl restart chrony
# 定时兜底:每 10 分钟强制步进校准一次(与 Rocky 部分相同,chronyd 常驻已持续
# 自动同步,此条为虚拟机时钟漂移等场景的保险;root 走本地 socket 无需 -a 认证)
# 幂等:cron 里已有该任务则不重复追加
grep -q 'chronyc makestep' /var/spool/cron/root 2>/dev/null || \
echo "*/10 * * * * /usr/bin/chronyc makestep &>/dev/null" >> /var/spool/cron/root
timedatectl | grep -E 'Time zone|synchronized'
# ===== 4. 关闭 ufw 防火墙(三个版本默认均未启用,幂等)=====
if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q 'Status: active'; then
ufw disable
fi
# AppArmor 保留不关:Ubuntu 用 AppArmor 而非 SELinux,默认策略不影响运维操作
# ===== 5. 关闭 swap =====
# 三个版本 Server 最小化安装默认都有 /swap.img(大小不一),处理方式与 Rocky 相同
# 幂等:跳过已注释的行,避免重复执行时加多层 #
swapoff -a
sed -i '/^\s*#/!{ /\sswap\s/s/^/#/ }' /etc/fstab
# 若存在 zram 设备(个别镜像带),一并卸载
if lsmod 2>/dev/null | grep -q '^zram'; then
rmmod zram 2>/dev/null || true
fi
free -m | awk '/^Swap/ {print "Swap(MB): " $3 " (应为 0)"}'
# ===== 6. 修改系统最大文件描述符(与 Rocky 相同,幂等)=====
sed -i '/nofile 65535/d' /etc/security/limits.conf
cat >> /etc/security/limits.conf <<'EOF'
root soft nofile 65535
root hard nofile 65535
* soft nofile 65535
* hard nofile 65535
EOF
# ===== 7. 安装 glances / mycli =====
# 版本差异:24.04/26.04 启用 PEP668(externally-managed),系统级 pip 装包必须加
# --break-system-packages;22.04 的旧 pip 不认这个参数,脚本自动判断
apt-get install -y python3-pip
PIP_EXTRA=""
if ls /usr/lib/python3*/EXTERNALLY-MANAGED >/dev/null 2>&1; then
PIP_EXTRA="--break-system-packages"
fi
pip3 install ${PIP_EXTRA} glances mycli==1.24.0
else
echo "已跳过系统级优化(换源/工具/chrony/ufw/swap/limits/glances):需要 root,请用 sudo 重新执行补全"
fi
# ============================================================
# 第二部分:用户级优化(写给目标用户,root/普通用户执行都会做)
# ============================================================
# ===== 8. 历史命令显示时间(与 Rocky 相同,幂等)=====
# 注意:Ubuntu 的全局 bashrc 是 /etc/bash.bashrc(Rocky 才是 /etc/bashrc),这里写目标用户的
sed -i '/HISTTIMEFORMAT/d' "${TARGET_HOME}/.bashrc"
cat >> "${TARGET_HOME}/.bashrc" << 'EOF'
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
export HISTTIMEFORMAT
EOF
# sed -i 重建文件会导致属主变 root,改回目标用户
chown "${TARGET_USER}:${TARGET_USER}" "${TARGET_HOME}/.bashrc"
# ===== 9. 设置 pip 国内源(写给目标用户)=====
[ -d "${TARGET_HOME}/.pip" ] || mkdir -p "${TARGET_HOME}/.pip"
cat > "${TARGET_HOME}/.pip/pip.conf" << EOF
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
EOF
chown -R "${TARGET_USER}:${TARGET_USER}" "${TARGET_HOME}/.pip"
# ===== 10. 安装并配置 grc(源码安装 + 命令输出着色,按目标用户登录 shell 写不同配置)=====
# 安装:clone 官方仓库 + 官方 install.sh(仅 root 可做;普通用户模式下 grc 已由 root 装好则直接配置)
# 安装路径:bin→/usr/bin、conf→/usr/share/grc、grc.conf→/etc、grc.sh→/etc/profile.d/、grc.zsh→/etc
# 配置:依据官方 README/源码,bash 的 grc.sh 必须先设 GRC_ALIASES=true 才会创建别名(grc.zsh 无此要求);
# bash 启用文件 /etc/profile.d/grc.sh(源码与 Debian 包默认位置),zsh 为 /etc/grc.zsh
# 幂等:已安装/已配置则跳过;候选路径逐个探测,兼容源码安装(/usr/local)与发行版包的路径差异
if ! command -v grc >/dev/null 2>&1; then
if [ "$IS_ROOT" -eq 1 ]; then
rm -rf /tmp/grc-src
# clone 和安装一条链:任一步失败都走 else 分支提示跳过,不会中断脚本
if git clone --depth 1 https://github.com/garabik/grc.git /tmp/grc-src \
&& (cd /tmp/grc-src && ./install.sh /usr ""); then
echo "grc: 源码安装完成"
else
echo "grc: git clone 或 install.sh 失败(网络原因?),跳过安装"
fi
else
echo "grc: 未安装且当前为普通用户,跳过安装(请用 sudo 执行本脚本安装)"
fi
fi
USER_SHELL="$(getent passwd "$TARGET_USER" | cut -d: -f7)"
GRC_CFG=""
RCFILE="${TARGET_HOME}/.bashrc"
case "$USER_SHELL" in
*/zsh)
RCFILE="${TARGET_HOME}/.zshrc"
for f in /etc/grc.zsh /usr/local/etc/grc.zsh /usr/share/grc/grc.zsh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
*)
for f in /etc/profile.d/grc.sh /etc/grc.sh /usr/local/etc/profile.d/grc.sh; do
if [ -f "$f" ]; then GRC_CFG="$f"; break; fi
done
;;
esac
if [ -n "$GRC_CFG" ] && ! grep -q 'grc' "$RCFILE" 2>/dev/null; then
if [ "$(basename "$GRC_CFG")" = "grc.sh" ]; then
echo 'GRC_ALIASES=true' >> "$RCFILE"
fi
echo "[ -s $GRC_CFG ] && . $GRC_CFG" >> "$RCFILE"
# 新建的 rc 文件属主会变成 root,改回目标用户
if [ "$IS_ROOT" -eq 1 ] && [ "$TARGET_USER" != "root" ]; then
chown "${TARGET_USER}:${TARGET_USER}" "$RCFILE"
fi
fi
# ===== 11. 设置别名(幂等,写给目标用户)=====
grep -q "alias n=" "${TARGET_HOME}/.bashrc" || cat >> "${TARGET_HOME}/.bashrc" << EOF
alias n='netstat -ntpl'
alias u='netstat -nupl'
alias a='netstat -natupl'
EOF
echo "Ubuntu ${UBU_VER} 优化完成。swap/防火墙/时间同步已立即生效,别名重新登录后生效(无需重启)"

