#!/bin/bash################################################# Ubuntu 初始化配置脚本(重构版)# 说明:所有分组按功能划分,每组前有注释说明用途与注意点# 用法:以普通用户身份执行,脚本会自动使用 sudo 提权################################################# set -u:未定义变量报错;pipefail:管道任一环节失败视为失败# 注意:不用 set -e —— apt 等命令非零退出是常态,显式处理更可控set-uopipefail# 全局非交互,避免 apt/dpkg 弹窗卡死exportDEBIAN_FRONTENDnoninteractive################################################# 0. 基础环境变量与守卫################################################# 用 id -un 比 whoami 更可靠(whoami 在部分容器里不可用)NOW_USER_NAME$(id-un)# getent 比 echo ~ 可靠:不受 shell 别名/HOME 篡改影响NOW_USER_HOME$(getentpasswd$NOW_USER_NAME|cut-d:-f6)NOW_USER_BASHRC${NOW_USER_HOME}/.bashrc# root 用户直接退出:本脚本设计为普通用户运行if[$NOW_USER_NAMEroot];thenecho[跳过] 当前为 root,无需执行用户级配置exit0fi# sudo 连通性预检:提前失败比中途失败更友好if!sudo-ntrue2/dev/null;thenecho[提示] 需要 sudo 权限,请输入密码:sudo-v||{echo[错误] 无法获取 sudo 权限;exit1;}fi# 标记:免密 sudo 是否由本脚本创建(用于第 10 步决定是否撤销)SUDOERS_CREATED0################################################# 1. 配置免密码 sudo################################################# 目标:让当前用户可以无密码 sudo(方便后续自动化)# 注意:# - 文件名用 90- 前缀,避免与系统默认文件冲突# - 用户名中的 . 会被 sudo 视为特殊字符,替换成 _# - 必须 chmod 0440,否则 sudo 直接忽略# - 必须 visudo -cf 校验,语法错会锁死 sudo# - 若文件已存在(用户原有),不动它,也不在第 10 步撤销SUDOERS_FILE/etc/sudoers.d/90-${NOW_USER_NAME//./_}if[!-f$SUDOERS_FILE];thenecho[1/11] 配置免密码 sudo:$SUDOERS_FILEprintf%s ALL(ALL) NOPASSWD: ALL\n$NOW_USER_NAME\|sudotee$SUDOERS_FILE/dev/nullsudochmod0440$SUDOERS_FILE# 校验失败立即回滚,避免留下损坏的 sudoersif!sudovisudo-cf$SUDOERS_FILE;thenecho[错误] sudoers 校验失败,已回滚sudorm-f$SUDOERS_FILEexit1fiSUDOERS_CREATED1elseecho[1/11] 免密码 sudo 已存在(非本脚本创建),跳过并保留fi################################################# 2. 共享 shell 提示符给 root(只共享 prompt,不复制整个 .bashrc)################################################# 说明:# 原脚本直接 cp 用户 .bashrc 给 root,会把用户的 alias/umask/PATH# 污染到 root 环境,存在安全风险# 这里改为只抽取与提示符/颜色相关的部分SHARED_PROMPT/etc/.color.bashrcif[!-f$SHARED_PROMPT];thenecho[2/11] 生成共享提示符:$SHARED_PROMPTsudotee$SHARED_PROMPT/dev/nullEOF # 共享的交互式提示符配置(被用户与 root 共同 source) # 只在交互式 shell 中生效 case $- in *i*) ;; *) return 2/dev/null || exit 0 ;; esac # 彩色 ls / grep alias lsls --colorauto alias grepgrep --colorauto # 彩色 PS1:用户名主机:路径$ PS1\[\e[1;32m\]\u\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ EOFsudochmod0644$SHARED_PROMPT# root 的 .bashrc 引入共享文件(若未引入)if!sudogrep-qF$SHARED_PROMPT/root/.bashrc2/dev/null;thenprintf\n[ -f %s ] . %s\n$SHARED_PROMPT$SHARED_PROMPT\|sudotee-a/root/.bashrc/dev/nullfi# 当前用户的 .bashrc 引入共享文件if!grep-qF$SHARED_PROMPT$NOW_USER_BASHRC2/dev/null;thenprintf\n[ -f %s ] . %s\n$SHARED_PROMPT$SHARED_PROMPT\$NOW_USER_BASHRCfielseecho[2/11] 共享提示符已存在,跳过fi################################################# 3. 关闭 GNOME 锁屏(仅当存在图形会话时)################################################# 说明:在无 GUI / 无 dbus 会话时 gsettings 会报错,故用 || true 忽略ifcommand-vgsettings/dev/null21;thenecho[3/11] 关闭 GNOME 锁屏gsettingssetorg.gnome.desktop.screensaver lock-enabledfalse2/dev/null||trueelseecho[3/11] 未检测到 gsettings,跳过fi################################################# 4. 配置系统级环境变量################################################# 关键区分:# /etc/environment.d/*.conf → 只接受 KEYVALUE,不支持 shell 语法# /etc/profile.d/*.sh → 由登录 shell source,可写 shell 逻辑# 原脚本把 shell 逻辑塞进 environment.d,导致全部失效。此处拆开。echo[4/11] 写入 /etc/environment.d/global.confsudomkdir-p/etc/environment.dsudotee/etc/environment.d/global.conf/dev/nullEOF # systemd 环境变量:纯 KEYVALUE,不要 export / 不要 $(...) LANGzh_CN.UTF-8 LC_CTYPEzh_CN.UTF-8 LC_ALLzh_CN.UTF-8 # 输入法(Wayland 下 GTK/QT 都走 ibus) GTK_IM_MODULEibus QT_IM_MODULEibus XMODIFIERSimibus XIMibus XIM_PROGRAMibus EOF# 注意:XDG_RUNTIME_DIR 由 pam_systemd 管理,写死会破坏 Wayland/多用户# 这里故意不设置echo[4/11] 写入 /etc/profile.d/99-dev.shsudotee/etc/profile.d/99-dev.sh/dev/nullEOF # 登录 shell 环境变量(可包含 shell 逻辑) # ---------- Rust ---------- export RUSTUP_DIST_SERVERhttps://rsproxy.cn export RUSTUP_UPDATE_ROOThttps://rsproxy.cn/rustup export RUSTUP_HOME/opt/rust/rustup export CARGO_HOME/opt/rust/cargo # cargo 自带 env 脚本,存在才 source [ -f $CARGO_HOME/env ] . $CARGO_HOME/env # ---------- Java ---------- export JAVA_HOME/opt/openlogic-openjdk-17.0.1710-linux-x64 export JRE_HOME$JAVA_HOME/jre # ---------- Maven ---------- export MAVEN_HOME/opt/apache-maven-3.9.11 # ---------- PATH 统一拼接(避免重复 ${PATH} 引用) ---------- # 顺序:JAVA MAVEN RUST 系统 case :$PATH: in *:$JAVA_HOME/bin:*) ;; # 已包含则跳过 *) export PATH$JAVA_HOME/bin:$MAVEN_HOME/bin:$CARGO_HOME/bin:$RUSTUP_HOME/bin:$PATH ;; esac # ---------- Git 全局配置(仅当 git 存在) ---------- if command -v git /dev/null 21; then git config --global user.name allwayup163.com git config --global user.email allwayup163.com # 使用 store 会明文保存凭据到 ~/.git-credentials,请知晓风险 git config --global credential.helper store # --unset 若不存在会返回非零,用 || true 忽略 git config --global --unset http.proxy 2/dev/null || true git config --global --unset https.proxy 2/dev/null || true # 警告:关闭 SSL 校验会降低安全性,仅内网/测试环境建议开启 # git config --global http.sslVerify false # git config --global https.sslVerify false fi EOFsudochmod0644 /etc/profile.d/99-dev.sh################################################# 5. 配置全局 vim################################################# 说明:# - 用 heredoc 直接写文件,比多层 $(...) 拼接更清晰# - 符号链接让所有用户共享 /etc/vimrc# - ~ 由当前 shell 展开,故不加 sudo 创建家目录内的链接echo[5/11] 写入 /etc/vimrcsudomkdir-p/etc/vimsudotee/etc/vimrc/dev/nullEOF ---------- 语法与插件 ---------- syntax on filetype plugin indent on set nocompatible set backgrounddark ---------- 搜索 ---------- set hlsearch set ignorecase set smartcase ---------- 缩进 ---------- set expandtab set tabstop4 set shiftwidth4 set autoindent set smartindent ---------- 界面 ---------- set cursorline set mouser ---------- 粘贴时自动关闭自动缩进 ---------- if has(autocmd) autocmd InsertEnter * set paste autocmd InsertEnter * set noautoindent autocmd InsertEnter * set nosmartindent autocmd InsertLeave * set autoindent autocmd InsertLeave * set smartindent autocmd InsertLeave * set nopaste endif EOFsudochmod0644 /etc/vimrc# 清理旧的 vimrc 与软链,重建sudorm-rf/etc/.vimrc /etc/vim/vimrcsudoln-sf/etc/vimrc /etc/.vimrcsudoln-sf/etc/vimrc /etc/vim/vimrc# 用户级链接:不使用 sudo,保证属主为当前用户rm-f$NOW_USER_HOME/.vimrc/root/.vimrc2/dev/null||trueln-sf/etc/vimrc$NOW_USER_HOME/.vimrcsudoln-sf/etc/vimrc /root/.vimrc################################################# 6. 重命名家目录下的中文目录为英文################################################# 说明:# - 通过遍历 /etc/passwd 获取真实用户(UID1000),比 ls /home 更可靠# - 修正原脚本 cp -rf ${SRC}/* 通配符未展开的问题(用 ${SRC}/.)# - 更新 ~/.config/user-dirs.dirs 让 xdg-user-dirs 保持一致echo[6/11] 重命名用户家目录(中文 → 英文)# 单个目录迁移:优先移动;若目标已存在则合并内容move_or_merge(){localsrc$1dst$2owner$3[-e$src]||{sudomkdir-p$dst;return;}if[-e$dst];then# 目标已存在 → 合并内容后删除源sudocp-rf$src/.$dst/sudorm-rf$srcelsesudomv$src$dstfisudochown-R$owner:$owner$dst}# 映射:中文名 → 英文名declare-ADIR_MAP([公共]common[模板]template[视频]video# 原脚本误拼 vedio,这里修正[图片]photo[文档]document[下载]download[音乐]music[桌面]desktop)whileIFS:read-ruser _ uid _ _ home _;do# 只处理普通用户(UID 1000)且家目录存在[$uid-ge1000]2/dev/null||continue[-d$home]||continue# 更新 xdg 用户目录配置UDC$home/.config/user-dirs.dirsif[-f$UDC];thenforzhin${!DIR_MAP[]};doen${DIR_MAP[$zh]}sudosed-is#/$zh#/$en#g$UDCdonefi# 移动/合并目录forzhin${!DIR_MAP[]};doen${DIR_MAP[$zh]}move_or_merge$home/$zh$home/$en$userdonedone/etc/passwd################################################# 7. 配置 APT 源(按实际发行版代号动态生成)################################################# 说明:# - 用 /etc/os-release 的 VERSION_CODENAME,避免硬编码错误版本# - 使用 https 而非 http,防中间人篡改# - Ubuntu 24.04 原生使用 deb822 格式(.sources)# - 不启用 -proposed(预发布版,稳定性差)echo[7/11] 配置 APT 源# 备份旧的 sources.list(如果存在)if[-f/etc/apt/sources.list];thensudomv/etc/apt/sources.list /etc/apt/sources.list.backup.$(date%s)fi# 从 os-release 读取代号# shellcheck disableSC1091./etc/os-releaseSUITE${VERSION_CODENAME:-jammy}sudotee/etc/apt/sources.list.d/ubuntu.sources/dev/nullEOF # 阿里云镜像(HTTPS) Types: deb URIs: https://mirrors.aliyun.com/ubuntu/ Suites:${SUITE}${SUITE}-updates${SUITE}-backports${SUITE}-security Components: main restricted universe multiverse Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg EOF# 如果确实需要源码源,再单独加 deb-src 段(日常可省略以加快 update)################################################# 8. APT 更新与基础包安装################################################# 说明:统一加 -y 与非交互;单个命令失败不终止脚本(用 || true)echo[8/11] APT 更新与基础工具安装sudoapt-getupdate||truesudoapt-get-y--fix-brokeninstall||truesudoapt-get-y-finstall||truesudoapt-get-yupgrade||truesudoapt-get-yautoremove||true# 基础工具sudoapt-getinstall-yvimgitwgetcurlmousepad||true################################################# 9. 输入法:ibus libpinyin################################################# 说明:# - 用引号包裹 fcitx* 避免 glob 未匹配时被当成字面量传给 apt# - im-config -s 在无图形会话时可能报错,故 || trueecho[9/11] 安装并配置 ibus 输入法sudoapt-get-yremovefcitx*||truesudoapt-get-yautoremove||truesudoapt-getinstall-y\dbus-x11\im-config\ibus\ibus-libpinyin\ibus-gtk\ibus-gtk3\ibus-wayland||true# 设置 ibus 为默认输入法框架(需图形会话,失败不致命)im-config-sibus2/dev/null||sudoim-config-sibus2/dev/null||true################################################# 10. 撤销本次脚本创建的免密 sudo################################################# 说明:# - 第 1 步建立免密只是为了本次脚本自动化# - 只有脚本这次创建的(SUDOERS_CREATED1)才撤销# - 用户原有或系统自带的文件一律不动,避免破坏既有配置echo[10/11] 检查是否需要撤销免密 sudoif[$SUDOERS_CREATED-eq1][-f$SUDOERS_FILE];thensudorm-f$SUDOERS_FILEecho 已撤销:$SUDOERS_FILEelseecho 无需撤销(非脚本创建或文件已不存在)fi################################################# 11. 完成################################################echo[11/11] 全部完成echoecho后续建议:echo 1. 重新登录或执行 source /etc/profile.d/99-dev.sh 让环境变量生效echo 2. 中文语言支持如未安装,可执行:echo sudo apt-get install -y language-pack-zh-hans fonts-noto-cjkecho 3. 输入法首次使用需在图形会话下运行:ibus-setupecho 4. 本次免密 sudo 已在第 10 步自动撤销;echo 若希望长期免密,请手动执行:echo echo $NOW_USER_NAMEALL(ALL) NOPASSWD: ALL | sudo tee$SUDOERS_FILEecho sudo chmod 0440$SUDOERS_FILE