From 67291a9a5085ec14b9efbcee5b1462a192d1c6f7 Mon Sep 17 00:00:00 2001 From: laily Date: Thu, 25 Jun 2026 15:09:35 +0800 Subject: [PATCH] feat: add script catalog and -h help for all dotfile scripts Introduce ff script -h with auto descriptions, shared help.sh, and usage flags across script/*.sh. Co-authored-by: Cursor --- fish/completions/ff.fish | 32 +++--- fish/functions/execute-script.fish | 77 ++++++--------- fish/functions/script-catalog.fish | 126 ++++++++++++++++++++++++ script/brew-clean.sh | 27 ++++++ script/brew-install.sh | 22 +++++ script/clip-copy.sh | 66 +++++++++---- script/common/help.sh | 11 +++ script/croncheck.sh | 34 +++++-- script/cursor-init.sh | 29 +++++- script/debian-init.sh | 20 +++- script/debian-tuna.sh | 18 ++++ script/dirsize.sh | 1 + script/dns-clean.sh | 25 ++++- script/docker-clean.sh | 23 +++++ script/docker-group.sh | 14 ++- script/docker-install.sh | 28 +++++- script/firacode-install.sh | 21 ++++ script/fish-init.sh | 40 +++++--- script/font-install.py | 78 ++++++++++----- script/ghostty-init.sh | 27 +++++- script/git-init.sh | 28 +++++- script/gvm-install.sh | 43 ++++----- script/init-default-user.sh | 23 ++++- script/lazygit-install.sh | 150 +++++++++++++++++------------ script/mihomo-install.sh | 82 +++++++++------- script/mise-install.sh | 55 ++++++----- script/nosudo.sh | 14 ++- script/nvim-init.sh | 31 ++++-- script/nvim-install.sh | 48 +++++---- script/nvm-install.sh | 32 +++++- script/python-install.sh | 43 ++++++--- script/scan-ip.sh | 45 +++++---- script/scan-port.sh | 76 +++++++-------- script/tmux-init.sh | 37 +++++-- script/toggle-region.sh | 27 +++++- script/zsh-init.sh | 45 ++++++--- 36 files changed, 1056 insertions(+), 442 deletions(-) create mode 100644 fish/functions/script-catalog.fish create mode 100644 script/common/help.sh diff --git a/fish/completions/ff.fish b/fish/completions/ff.fish index cd7b61f..d503fb4 100644 --- a/fish/completions/ff.fish +++ b/fish/completions/ff.fish @@ -65,22 +65,24 @@ complete -c ff -n "__fish_seen_subcommand_from ssh copy; and test (count (comman # script 子命令的补全 - 动态列出 script 目录下的脚本 function __ff_list_scripts - set -l script_dir ~/.dotfile/script - if test -d $script_dir - # 列出 .sh 文件 - for script in $script_dir/*.sh - if test -f $script - basename $script | string replace -r '\.sh$' '' - end - end - # 列出 .py 文件 - for script in $script_dir/*.py - if test -f $script - basename $script | string replace -r '\.py$' '' - end - end + if not functions -q script-list-names + source ~/.dotfile/fish/functions/script-catalog.fish + end + script-list-names +end + +function __ff_complete_scripts + if not functions -q script-extract-description + source ~/.dotfile/fish/functions/script-catalog.fish + end + for name in (script-list-names) + set -l path (script-resolve-path $name) + set -l desc (script-extract-description $path) + printf "%s\t%s\n" $name $desc end end -complete -c ff -n "__fish_seen_subcommand_from script" -a '(__ff_list_scripts)' -d "Script name" +complete -c ff -n "__fish_seen_subcommand_from script; and test (count (commandline -opc)) -eq 2" -s h -l help -d "List all scripts and descriptions" +complete -c ff -n "__fish_seen_subcommand_from script; and test (count (commandline -opc)) -eq 2" -a help -d "List all scripts and descriptions" +complete -c ff -n "__fish_seen_subcommand_from script; and not __fish_seen_subcommand_from help -h --help (__ff_list_scripts)" -a '(__ff_complete_scripts)' -d "Script name" diff --git a/fish/functions/execute-script.fish b/fish/functions/execute-script.fish index 6a9a922..70ce717 100644 --- a/fish/functions/execute-script.fish +++ b/fish/functions/execute-script.fish @@ -1,65 +1,42 @@ # Execute script from script directory function execute-script - # 执行 script 目录下的脚本 + if not functions -q script-show-catalog + source ~/.dotfile/fish/functions/script-catalog.fish + end + set -l dotfile_dir ~/.dotfile set -l script_dir $dotfile_dir/script set -l script_name $argv[1] - - if test -z "$script_name" - echo "Error: No script name specified" - echo "Usage: execute-script [args...]" - echo "" - echo "Available scripts:" - if test -d $script_dir - for script in $script_dir/*.sh $script_dir/*.py - if test -f $script - set -l basename (basename $script) - echo " $basename" - end - end + + # ff script -h / --help / help + if test -z "$script_name"; or contains -- $script_name -h --help help + if test -z "$script_name"; or test (count $argv) -eq 1 + script-show-catalog + return 0 end + end + + if test -z "$script_name" + script-show-catalog return 1 end - - # 尝试不同的扩展名 - set -l script_path "" - for ext in sh py - set -l test_path $script_dir/$script_name.$ext - if test -f $test_path - set script_path $test_path - break - end - end - - # 如果没找到,尝试直接使用提供的名称 - if test -z "$script_path" - set script_path $script_dir/$script_name - end - - if test -f $script_path + + set -l script_path (script-resolve-path $script_name 2>/dev/null; or echo "") + + if test -n "$script_path" -a -f "$script_path" echo "Running: $script_path" if test -x $script_path $script_path $argv[2..-1] + else if string match -q "*.py" $script_path + python3 $script_path $argv[2..-1] else - # 根据文件类型选择解释器 - if string match -q "*.py" $script_path - python3 $script_path $argv[2..-1] - else - bash $script_path $argv[2..-1] - end + bash $script_path $argv[2..-1] end - else - echo "Error: Script not found: $script_name" - echo "Available scripts:" - if test -d $script_dir - for script in $script_dir/*.sh $script_dir/*.py - if test -f $script - set -l basename (basename $script) - echo " $basename" - end - end - end - return 1 + return $status end -end + echo "Error: Script not found: $script_name" + echo "" + script-show-catalog + return 1 +end diff --git a/fish/functions/script-catalog.fish b/fish/functions/script-catalog.fish new file mode 100644 index 0000000..47c07ba --- /dev/null +++ b/fish/functions/script-catalog.fish @@ -0,0 +1,126 @@ +# 列举 ~/.dotfile/script 下脚本及从文件头注释提取的简短说明 + +function script-extract-description --argument filepath + if not test -f "$filepath" + echo "(无描述)" + return + end + + set -l past_shebang 0 + set -l candidates + for line in (cat "$filepath") + if string match -qr '^#!/' -- $line + set past_shebang 1 + continue + end + if test $past_shebang -eq 0 + continue + end + + set line (string trim $line) + if test -z "$line" + if test (count $candidates) -gt 0 + break + end + continue + end + if not string match -qr '^#' -- $line + break + end + + set line (string replace -r '^#\s*' '' $line) + if string match -qr '^---' -- $line + continue + end + if string match -qr '^(用法|Usage|示例|Example)' -- $line + continue + end + if string match -qr '^\./' -- $line + continue + end + if string match -qr '^https?://' -- $line + continue + end + # 行内 “# 用法” 类注释 + if string match -qr '#\s*(用法|Usage)' -- $line + continue + end + + set candidates $candidates $line + if test (count $candidates) -ge 8 + break + end + end + + if test (count $candidates) -eq 0 + echo "(无描述)" + return + end + + for c in $candidates + if string match -qr '(脚本|安装|初始化|清理|扫描|切换|配置|用于|镜像|凭据|用户组|免密|Install|Init|Clean|Toggle)' -- $c + echo $c + return + end + end + + for c in $candidates + if test (string length $c) -ge 10 + echo $c + return + end + end + + echo $candidates[1] +end + +function script-list-names + set -l script_dir ~/.dotfile/script + set -l names + if not test -d $script_dir + return + end + + for script in $script_dir/*.sh $script_dir/*.py + if test -f $script + set -l name (basename $script) + set name (string replace -r '\.(sh|py)$' '' $name) + set names $names $name + end + end + + string join \n $names | sort +end + +function script-resolve-path --argument name + set -l script_dir ~/.dotfile/script + for ext in sh py + set -l path "$script_dir/$name.$ext" + if test -f $path + echo $path + return 0 + end + end + if test -f "$script_dir/$name" + echo "$script_dir/$name" + return 0 + end + return 1 +end + +function script-show-catalog + set -l script_dir ~/.dotfile/script + echo "Usage: ff script [args...]" + echo "" + echo "Scripts in $script_dir:" + echo "" + + for name in (script-list-names) + set -l path (script-resolve-path $name) + set -l desc (script-extract-description $path) + printf " %-22s %s\n" $name $desc + end + + echo "" + echo "提示: ff script -h 查看单个脚本内的详细帮助" +end diff --git a/script/brew-clean.sh b/script/brew-clean.sh index a9bf588..0f5c311 100755 --- a/script/brew-clean.sh +++ b/script/brew-clean.sh @@ -1 +1,28 @@ #!/bin/bash +# 清理 Homebrew 缓存与无用文件 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: brew-clean + +执行 Homebrew 清理(brew cleanup),删除旧版本与缓存。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail + +if ! command -v brew &>/dev/null; then + echo "错误: 未找到 brew,请先安装 Homebrew" >&2 + exit 1 +fi + +brew cleanup diff --git a/script/brew-install.sh b/script/brew-install.sh index 63488e3..e2e3d22 100755 --- a/script/brew-install.sh +++ b/script/brew-install.sh @@ -1,3 +1,25 @@ +#!/bin/bash +# 使用国内镜像安装 Homebrew + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: brew-install + +通过中科大镜像安装 Homebrew(设置 HOMEBREW_* 镜像环境变量后执行官方安装脚本)。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail + export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.ustc.edu.cn/brew.git" export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.ustc.edu.cn/homebrew-core.git" export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.ustc.edu.cn/homebrew-bottles" diff --git a/script/clip-copy.sh b/script/clip-copy.sh index 0f2e265..5b1fc1f 100755 --- a/script/clip-copy.sh +++ b/script/clip-copy.sh @@ -1,31 +1,57 @@ #!/usr/bin/env bash -# Copy stdin to the system clipboard (macOS / Linux X11 / Linux Wayland). +# 将标准输入复制到系统剪贴板(macOS / Linux X11 / Wayland) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: clip-copy + +从 stdin 读取内容并写入系统剪贴板。 + +支持: + macOS: pbcopy + Wayland: wl-copy + X11: xclip / xsel + +选项: + -h, --help 显示此帮助 + +示例: + echo hello | clip-copy + cat file.txt | clip-copy +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -euo pipefail if command -v pbcopy >/dev/null 2>&1; then - pbcopy + pbcopy elif [ "${XDG_SESSION_TYPE:-}" = wayland ] || [ -n "${WAYLAND_DISPLAY:-}" ]; then - if command -v wl-copy >/dev/null 2>&1; then - wl-copy --no-newline - else - echo "clip-copy: wl-copy not found (Wayland session)" >&2 - exit 1 - fi + if command -v wl-copy >/dev/null 2>&1; then + wl-copy --no-newline + else + echo "clip-copy: wl-copy not found (Wayland session)" >&2 + exit 1 + fi elif [ -n "${DISPLAY:-}" ]; then - if command -v xclip >/dev/null 2>&1; then - xclip -selection clipboard - elif command -v xsel >/dev/null 2>&1; then - xsel --clipboard --input - else - echo "clip-copy: xclip or xsel not found (DISPLAY=$DISPLAY)" >&2 - exit 1 - fi + if command -v xclip >/dev/null 2>&1; then + xclip -selection clipboard + elif command -v xsel >/dev/null 2>&1; then + xsel --clipboard --input + else + echo "clip-copy: xclip or xsel not found (DISPLAY=$DISPLAY)" >&2 + exit 1 + fi elif command -v xclip >/dev/null 2>&1; then - xclip -selection clipboard + xclip -selection clipboard elif command -v xsel >/dev/null 2>&1; then - xsel --clipboard --input + xsel --clipboard --input else - echo "clip-copy: no clipboard tool (pbcopy / wl-copy / xclip / xsel)" >&2 - exit 1 + echo "clip-copy: no clipboard tool (pbcopy / wl-copy / xclip / xsel)" >&2 + exit 1 fi diff --git a/script/common/help.sh b/script/common/help.sh new file mode 100644 index 0000000..5b8c6ce --- /dev/null +++ b/script/common/help.sh @@ -0,0 +1,11 @@ +# 供 script/*.sh source:统一处理 -h / --help +# 调用方需先定义 usage() 函数 + +dotfile_help_requested() { + [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] +} + +dotfile_show_help() { + usage + exit 0 +} diff --git a/script/croncheck.sh b/script/croncheck.sh index 7fdbc47..47563f3 100644 --- a/script/croncheck.sh +++ b/script/croncheck.sh @@ -1,21 +1,40 @@ #!/bin/bash +# 执行命令并在失败时通过飞书 Webhook 发送通知 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: croncheck [args...] + +执行给定命令;若退出码非 0,向配置的飞书 Webhook 发送失败通知。 +最终以原命令的退出码退出。 + +选项: + -h, --help 显示此帮助 + +示例: + croncheck /path/to/backup.sh + croncheck docker compose up -d +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help -# 检查参数数量 if [ "$#" -lt 1 ]; then - echo "Usage: $0 [command_args...]" + echo "错误: 缺少要执行的命令" >&2 + usage exit 1 fi -# 获取 webhook URL WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/808eafd8-a79f-4af0-b730-8442cf15933b" -# 执行命令并捕获输出 OUTPUT=$("$@" 2>&1) EXIT_CODE=$? -# 如果命令失败,发送 webhook 通知 if [ $EXIT_CODE -ne 0 ]; then - # 准备 JSON 数据 JSON_DATA=$(cat < -# 示例: sudo docker-group xuser + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" set -euo pipefail @@ -17,12 +19,14 @@ usage() { 说明: - 用户需重新登录或 newgrp docker 后生效 - 安装 Docker 时 docker-install.sh 只会把当时的 $USER 加入组 + +选项: + -h, --help 显示此帮助 EOF - exit 1 } -[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage -[[ $# -eq 1 ]] || usage +dotfile_help_requested "${1:-}" && dotfile_show_help +[[ $# -eq 1 ]] || { usage; exit 1; } if [[ $EUID -ne 0 ]]; then echo "错误: 请使用 sudo 运行,例如: sudo $0 $1" diff --git a/script/docker-install.sh b/script/docker-install.sh index d9a6017..c61f103 100644 --- a/script/docker-install.sh +++ b/script/docker-install.sh @@ -1,4 +1,27 @@ #!/bin/bash +# 在 Debian/Ubuntu 上安装 Docker CE(支持国内/国外镜像源) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: docker-install + +在 Debian 或 Ubuntu 上安装 Docker CE 及 compose/buildx 插件。 +根据环境变量 CN_REGION 选择镜像源: + CN_REGION=1 清华镜像 + 其他 Docker 官方源 + +安装完成后将当前用户加入 docker 组。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e @@ -38,8 +61,6 @@ install_prerequisites() { sudo apt-get install -y ca-certificates curl gnupg lsb-release } -################################################################# -# 国内安装(使用清华镜像) install_docker_cn() { local repo_base="https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/${DISTRO}" local keyring="/usr/share/keyrings/docker-archive-keyring.gpg" @@ -56,8 +77,6 @@ install_docker_cn() { sudo usermod -aG docker "$USER" } -################################################################# -# 国外安装(使用官方源) install_docker_intl() { local repo_base="https://download.docker.com/linux/${DISTRO}" local keyring="/etc/apt/keyrings/docker.asc" @@ -81,7 +100,6 @@ detect_distro remove_conflicting_packages install_prerequisites -# 根据环境变量选择安装方式(须在函数定义之后调用) if [ "$CN_REGION" = "1" ]; then echo "[INFO] 国内环境,使用清华镜像安装 Docker (${DISTRO}/${CODENAME})" install_docker_cn diff --git a/script/firacode-install.sh b/script/firacode-install.sh index 03b13cb..74f999c 100755 --- a/script/firacode-install.sh +++ b/script/firacode-install.sh @@ -1,4 +1,25 @@ #!/bin/bash +# 下载 Fira Code 字体压缩包并打开所在目录(需手动安装) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: firacode-install + +从 GitHub 下载 Fira Code v6.2 到 ~/Downloads/,并打开 Finder/文件管理器。 +需在图形界面中解压并安装字体文件。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail cd ~/Downloads/ wget https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip diff --git a/script/fish-init.sh b/script/fish-init.sh index bf81801..844f06f 100755 --- a/script/fish-init.sh +++ b/script/fish-init.sh @@ -1,4 +1,25 @@ #!/bin/bash +# 初始化 Fish:wrapper 配置、链接 functions/completions、安装 fisher/z + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: fish-init + +1. 确保 ~/.config/fish/config.fish source dotfile/fish/default.fish +2. 链接 dotfile 中 fish functions 与 completions +3. 安装 fisher、z 插件 +4. 将默认 shell 设为 fish + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help LOCAL_CONFIG="$HOME/.config/fish/config.fish" DEFAULT_PATH="$HOME/.dotfile/fish/default.fish" @@ -8,43 +29,36 @@ mkdir -p "$HOME/.config/fish" echo 'Setup fish config.fish wrapper' -# 旧版迁移:如果 config.fish 是指向 dotfile 的 symlink(老布局),移除 if [ -L "$LOCAL_CONFIG" ]; then link_target=$(readlink "$LOCAL_CONFIG") case "$link_target" in - */dotfile/fish/config.fish|*/dotfile/fish/default.fish) + */dotfile/fish/config.fish | */dotfile/fish/default.fish) echo " Removing legacy symlink: $LOCAL_CONFIG -> $link_target" rm "$LOCAL_CONFIG" ;; esac fi -# 不存在 -> 创建只含一行 source 的本地 wrapper -# 存在但未 source default.fish -> 在文件顶部插入 source 行 -# 已 source -> 跳过 if [ ! -f "$LOCAL_CONFIG" ]; then - echo "$SOURCE_LINE" > "$LOCAL_CONFIG" + echo "$SOURCE_LINE" >"$LOCAL_CONFIG" echo " Created $LOCAL_CONFIG (sources default.fish)" elif ! grep -qE '^[[:space:]]*source.*default\.fish' "$LOCAL_CONFIG"; then tmp=$(mktemp) - { echo "$SOURCE_LINE"; echo; cat "$LOCAL_CONFIG"; } > "$tmp" + { echo "$SOURCE_LINE"; echo; cat "$LOCAL_CONFIG"; } >"$tmp" mv "$tmp" "$LOCAL_CONFIG" echo " Prepended source line to existing $LOCAL_CONFIG" else echo " $LOCAL_CONFIG already sources default.fish, skip" fi -# 确保目标目录存在 echo 'Link fish functions and completions' mkdir -p "$HOME/.config/fish/functions" mkdir -p "$HOME/.config/fish/completions" -# 链接 functions 目录下的所有文件 for file in $HOME/.dotfile/fish/functions/*.fish; do if [ -f "$file" ]; then filename=$(basename "$file") target="$HOME/.config/fish/functions/$filename" - # 如果已存在且不是符号链接,备份 if [ -f "$target" ] && [ ! -L "$target" ]; then mv "$target" "$target.bak" fi @@ -53,12 +67,10 @@ for file in $HOME/.dotfile/fish/functions/*.fish; do fi done -# 链接 completions 目录下的所有文件 for file in $HOME/.dotfile/fish/completions/*.fish; do if [ -f "$file" ]; then filename=$(basename "$file") target="$HOME/.config/fish/completions/$filename" - # 如果已存在且不是符号链接,备份 if [ -f "$target" ] && [ ! -L "$target" ]; then mv "$target" "$target.bak" fi @@ -72,5 +84,5 @@ fish -c "fisher install jethrokuan/z" echo 'Set fish as default shell' fishpath=$(which fish) -echo $fishpath | sudo tee -a /etc/shells -chsh -s $fishpath +echo "$fishpath" | sudo tee -a /etc/shells +chsh -s "$fishpath" diff --git a/script/font-install.py b/script/font-install.py index 54acec1..00d0d9d 100644 --- a/script/font-install.py +++ b/script/font-install.py @@ -1,83 +1,109 @@ #!/usr/bin/env python3 +# 下载并安装 Sarasa Nerd / Fira Code 字体到用户字体目录 +import argparse import os -import sys import platform import subprocess -from typing import Dict, List, Optional +import sys import urllib.request import zipfile import shutil +from typing import Dict, List + + +def usage_text() -> str: + return """用法: font-install + +下载并安装以下字体到用户字体目录: + - Sarasa Gothic Nerd (SC) + - Fira Code + +macOS: ~/Library/Fonts +Linux: ~/.local/share/fonts(安装后尝试 fc-cache) + +选项: + -h, --help 显示此帮助 +""" + def get_font_dir() -> str: - """根据操作系统确定字体安装目录""" system = platform.system() - if system == "Darwin": # macOS + if system == "Darwin": return os.path.expanduser("~/Library/Fonts") - elif system == "Linux": + if system == "Linux": return os.path.expanduser("~/.local/share/fonts") - else: - raise OSError(f"不支持的操作系统: {system}") + raise OSError(f"不支持的操作系统: {system}") + def check_command(command: str) -> bool: - """检查命令是否存在""" return shutil.which(command) is not None + def download_file(url: str, dest_path: str) -> None: - """下载文件到指定路径""" try: urllib.request.urlretrieve(url, dest_path) except Exception as e: print(f"下载失败: {e}") sys.exit(1) + def get_top_level_dirs(zip_file: zipfile.ZipFile) -> List[str]: - """获取 ZIP 文件中的顶级目录""" - return list(set(item.split('/')[0] for item in zip_file.namelist() if '/' in item)) + return list(set(item.split("/")[0] for item in zip_file.namelist() if "/" in item)) + def has_single_top_dir(zip_file: zipfile.ZipFile) -> bool: - """检查 ZIP 文件是否只有一个顶级目录""" top_dirs = get_top_level_dirs(zip_file) - return len(top_dirs) == 1 and all(name.startswith(top_dirs[0] + '/') for name in zip_file.namelist()) + return len(top_dirs) == 1 and all( + name.startswith(top_dirs[0] + "/") for name in zip_file.namelist() + ) def unzip_file(zip_path: str, font_name: str) -> None: - """解压文件到指定路径,根据 ZIP 文件结构决定是否创建顶级目录""" - with zipfile.ZipFile(zip_path, 'r') as zip_ref: + with zipfile.ZipFile(zip_path, "r") as zip_ref: if has_single_top_dir(zip_ref): - # ZIP 文件有单一顶级目录,直接解压 zip_ref.extractall("/tmp") print(f"已解压 {font_name} 到现有顶级目录") else: - # 没有单一顶级目录,创建一个以字体名命名的目录 font_dir = os.path.join("/tmp", font_name) os.makedirs(font_dir, exist_ok=True) for file in zip_ref.namelist(): - if not file.startswith('__MACOSX'): # 排除 macOS 系统文件 + if not file.startswith("__MACOSX"): zip_ref.extract(file, font_dir) print(f"已解压 {font_name} 并创建新的顶级目录") def install_font(font_name: str, font_dir: str) -> None: - """安装字体到指定目录""" - # find ttf files to copy to the font_dir - ttf_files = [file for file in os.listdir(f"/tmp/{font_name}") if file.endswith(".ttf")] + ttf_files = [ + file for file in os.listdir(f"/tmp/{font_name}") if file.endswith(".ttf") + ] for ttf_file in ttf_files: shutil.copy(f"/tmp/{font_name}/{ttf_file}", font_dir) print(f"已安装 {font_name} 到 {font_dir}") + def refresh_font_cache() -> None: - """在 Linux 系统上刷新字体缓存""" if platform.system() == "Linux" and check_command("fc-cache"): - subprocess.run(["fc-cache", "-f"]) + subprocess.run(["fc-cache", "-f"], check=False) print("字体缓存已刷新。") else: print("警告:无法刷新字体缓存。您可能需要手动刷新。") + def main() -> None: + parser = argparse.ArgumentParser( + description="下载并安装 Sarasa Nerd / Fira Code 字体", + add_help=False, + ) + parser.add_argument("-h", "--help", action="store_true", help="显示帮助") + args, _ = parser.parse_known_args() + if args.help: + print(usage_text()) + return + fonts: Dict[str, str] = { - "SarasaNerd":"https://github.com/jonz94/Sarasa-Gothic-Nerd-Fonts/releases/download/v1.0.23-0/sarasa-fixed-sc-nerd-font.zip", - "FiraCode": "https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip" + "SarasaNerd": "https://github.com/jonz94/Sarasa-Gothic-Nerd-Fonts/releases/download/v1.0.23-0/sarasa-fixed-sc-nerd-font.zip", + "FiraCode": "https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip", } try: @@ -99,6 +125,6 @@ def main() -> None: refresh_font_cache() print("安装完成。请重启您的终端或应用程序以使用新字体。") + if __name__ == "__main__": main() - diff --git a/script/ghostty-init.sh b/script/ghostty-init.sh index 00984b7..730d041 100755 --- a/script/ghostty-init.sh +++ b/script/ghostty-init.sh @@ -1,4 +1,26 @@ #!/bin/bash +# 将 Ghostty 配置链接到 dotfile(macOS / Linux) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: ghostty-init + +将 ~/.dotfile/ghostty/config.ghostty 软链接到 Ghostty 配置目录: + macOS: ~/Library/Application Support/com.mitchellh.ghostty/ + Linux: $XDG_CONFIG_HOME/ghostty/ + +已有配置会备份为 *.backup.<时间戳>。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e @@ -9,7 +31,6 @@ NC='\033[0m' SRC="$HOME/.dotfile/ghostty/config.ghostty" -# 根据平台确定目标路径 if [[ "$(uname)" == "Darwin" ]]; then DEST="$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty" else @@ -20,22 +41,18 @@ fi echo -e "${YELLOW}初始化 Ghostty 配置...${NC}" echo -e "${YELLOW}目标路径:$DEST${NC}" -# 目标目录不存在则创建 mkdir -p "$(dirname "$DEST")" -# 已经是指向正确位置的软链接,无需操作 if [ -L "$DEST" ] && [ "$(readlink -f "$DEST")" = "$(readlink -f "$SRC")" ]; then echo -e "${GREEN}软链接已存在且指向正确位置,无需操作。${NC}" exit 0 fi -# 存在旧文件/目录则备份 if [ -e "$DEST" ]; then BACKUP="${DEST}.backup.$(date +%Y%m%d%H%M%S)" mv "$DEST" "$BACKUP" echo -e "${YELLOW}已将现有配置备份到 $BACKUP${NC}" elif [ -L "$DEST" ]; then - # 损坏的旧软链接 rm "$DEST" fi diff --git a/script/git-init.sh b/script/git-init.sh index dee44b1..d326d94 100755 --- a/script/git-init.sh +++ b/script/git-init.sh @@ -1,11 +1,31 @@ #!/bin/bash +# 将 ~/.gitconfig 链接到 dotfile,并设置 Linux 凭据脚本可执行 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: git-init + +备份已有 ~/.gitconfig,软链接到 ~/.dotfile/git/.gitconfig。 +同时为 git-credential-linux.sh 添加可执行权限。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail echo 'Init git config' src=$HOME/.gitconfig -if [ -f $src ]; then - mv $src $src.bak +if [ -f "$src" ]; then + mv "$src" "$src.bak" fi -ln -s $HOME/.dotfile/git/.gitconfig $src +ln -s "$HOME/.dotfile/git/.gitconfig" "$src" chmod +x "$HOME/.dotfile/git/git-credential-linux.sh" - diff --git a/script/gvm-install.sh b/script/gvm-install.sh index a688286..9d009f7 100644 --- a/script/gvm-install.sh +++ b/script/gvm-install.sh @@ -1,32 +1,31 @@ #!/bin/bash +# 安装 gvm(Go 版本管理)并配置 fish 与默认 Go 版本 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: gvm-install + +1. 安装 gvm +2. 为 fish 安装 fish-gvm、bass 插件 +3. 安装并设置 go1.24.0 为默认版本 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + set -e -# install gvm bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) -# install gvm on fish fish -c "fisher install JGAntunes/fish-gvm" fish -c "fisher install edc/bass" -# install go fish -c "gvm install go1.24.0 -B" fish -c "gvm use go1.24.0 --default" - -# # install neovim -# os_arch=$(uname -s)-$(uname -m) - -# case $os_arch in -# "Darwin-x86_64") -# cd /usr/local -# wget https://golang.google.cn/dl/go1.23.2.linux-amd64.tar.gz -# tar zxvf go1.23.2.linux-amd64.tar.gz -# ;; -# "Linux-x86_64"|"Linux-arm64") -# cd /tmp -# rm -rf nvim-linux64.tar.gz -# wget https://go.dev/dl/go1.23.2.linux-amd64.tar.gz -# tar zxvf go1.23.2.linux-amd64.tar.gz -# mv go /usr/local/ -# sudo ln -s /usr/local/go/bin/go/usr/local/bin/go -# ;; -# esac diff --git a/script/init-default-user.sh b/script/init-default-user.sh index 2dacd8e..c4621ea 100644 --- a/script/init-default-user.sh +++ b/script/init-default-user.sh @@ -1,9 +1,26 @@ #!/bin/bash -# 初始化默认用户脚本 -# 用于创建 ace 用户并给予 sudo 权限 +# 初始化默认用户脚本:创建 ace 用户并给予 sudo 权限 -set -e # 遇到错误时立即退出 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: sudo init-default-user + +创建用户 ace(若不存在),设置密码,加入 sudo 组, +并复制 ~/.dotfile 到该用户家目录。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -e # 检查是否以 root 身份运行 if [[ $EUID -ne 0 ]]; then diff --git a/script/lazygit-install.sh b/script/lazygit-install.sh index 4d9aacd..4f1f5eb 100644 --- a/script/lazygit-install.sh +++ b/script/lazygit-install.sh @@ -1,4 +1,27 @@ #!/bin/bash +# 安装 lazygit(brew / apt / GitHub Release 二进制) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: lazygit-install + +按顺序尝试安装 lazygit: + 1. Homebrew (macOS/Linux) + 2. apt (Debian/Ubuntu) + 3. 从 GitHub Release 下载到 ~/.local/bin + +若已安装则打印版本并退出。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e @@ -8,89 +31,88 @@ RED='\033[0;31m' NC='\033[0m' if command -v lazygit &>/dev/null; then - echo -e "${GREEN}lazygit 已安装:${NC}" - lazygit --version - exit 0 + echo -e "${GREEN}lazygit 已安装:${NC}" + lazygit --version + exit 0 fi install_via_brew() { - if ! command -v brew &>/dev/null; then - return 1 - fi - echo -e "${YELLOW}使用 Homebrew 安装 lazygit...${NC}" - brew install lazygit + if ! command -v brew &>/dev/null; then + return 1 + fi + echo -e "${YELLOW}使用 Homebrew 安装 lazygit...${NC}" + brew install lazygit } install_via_apt() { - if ! command -v apt-get &>/dev/null; then - return 1 - fi - echo -e "${YELLOW}使用 apt 安装 lazygit...${NC}" - sudo apt-get update - sudo apt-get install -y lazygit + if ! command -v apt-get &>/dev/null; then + return 1 + fi + echo -e "${YELLOW}使用 apt 安装 lazygit...${NC}" + sudo apt-get update + sudo apt-get install -y lazygit } -# 从 GitHub Release 解压到 ~/.local/bin(与 nvim/init.lua 中的 PATH 一致) install_via_release() { - local ver - ver=$(curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | - grep '"tag_name":' | - sed -E 's/.*"v([^"]+)".*/\1/') - if [[ -z "$ver" ]]; then - echo -e "${RED}无法解析 lazygit 最新版本号${NC}" >&2 - return 1 - fi + local ver + ver=$(curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | + grep '"tag_name":' | + sed -E 's/.*"v([^"]+)".*/\1/') + if [[ -z "$ver" ]]; then + echo -e "${RED}无法解析 lazygit 最新版本号${NC}" >&2 + return 1 + fi - local os_raw arch_raw asset url - os_raw=$(uname -s) - arch_raw=$(uname -m) + local os_raw arch_raw asset url + os_raw=$(uname -s) + arch_raw=$(uname -m) - case "${os_raw}-${arch_raw}" in - Darwin-arm64) asset="lazygit_${ver}_Darwin_arm64.tar.gz" ;; - Darwin-x86_64) asset="lazygit_${ver}_Darwin_x86_64.tar.gz" ;; - Linux-x86_64) asset="lazygit_${ver}_Linux_x86_64.tar.gz" ;; - Linux-aarch64 | Linux-arm64) asset="lazygit_${ver}_Linux_arm64.tar.gz" ;; - *) - echo -e "${RED}当前平台不支持自动下载二进制: ${os_raw} ${arch_raw}${NC}" >&2 - return 1 - ;; - esac + case "${os_raw}-${arch_raw}" in + Darwin-arm64) asset="lazygit_${ver}_Darwin_arm64.tar.gz" ;; + Darwin-x86_64) asset="lazygit_${ver}_Darwin_x86_64.tar.gz" ;; + Linux-x86_64) asset="lazygit_${ver}_Linux_x86_64.tar.gz" ;; + Linux-aarch64 | Linux-arm64) asset="lazygit_${ver}_Linux_arm64.tar.gz" ;; + *) + echo -e "${RED}当前平台不支持自动下载二进制: ${os_raw} ${arch_raw}${NC}" >&2 + return 1 + ;; + esac - url="https://github.com/jesseduffield/lazygit/releases/download/v${ver}/${asset}" - local tmpdir dest_bin - tmpdir=$(mktemp -d) - dest_bin="${HOME}/.local/bin" + url="https://github.com/jesseduffield/lazygit/releases/download/v${ver}/${asset}" + local tmpdir dest_bin + tmpdir=$(mktemp -d) + dest_bin="${HOME}/.local/bin" - echo -e "${YELLOW}下载 lazygit v${ver} (${asset})...${NC}" - curl -fsSL -o "${tmpdir}/${asset}" "${url}" - tar -xzf "${tmpdir}/${asset}" -C "${tmpdir}" + echo -e "${YELLOW}下载 lazygit v${ver} (${asset})...${NC}" + curl -fsSL -o "${tmpdir}/${asset}" "${url}" + tar -xzf "${tmpdir}/${asset}" -C "${tmpdir}" - mkdir -p "${dest_bin}" - mv -f "${tmpdir}/lazygit" "${dest_bin}/lazygit" - chmod +x "${dest_bin}/lazygit" - rm -rf "${tmpdir}" + mkdir -p "${dest_bin}" + mv -f "${tmpdir}/lazygit" "${dest_bin}/lazygit" + chmod +x "${dest_bin}/lazygit" + rm -rf "${tmpdir}" - echo -e "${GREEN}已安装到 ${dest_bin}/lazygit${NC}" - if [[ ":${PATH}:" != *":${dest_bin}:"* ]]; then - echo -e "${YELLOW}提示: 请将 ${dest_bin} 加入 PATH(例如在 ~/.zshrc 中 export PATH=\"\$HOME/.local/bin:\$PATH\")${NC}" - fi + echo -e "${GREEN}已安装到 ${dest_bin}/lazygit${NC}" + if [[ ":${PATH}:" != *":${dest_bin}:"* ]]; then + echo -e "${YELLOW}提示: 请将 ${dest_bin} 加入 PATH${NC}" + fi } main() { - if install_via_brew; then - : - elif install_via_apt; then - : - elif install_via_release; then - : - else - echo -e "${RED}lazygit 安装失败:未找到 brew/apt,且当前平台无法使用官方二进制发布包。${NC}" >&2 - exit 1 - fi + if install_via_brew; then + : + elif install_via_apt; then + : + elif install_via_release; then + : + else + echo -e "${RED}lazygit 安装失败${NC}" >&2 + exit 1 + fi - echo -e "${GREEN}lazygit 安装完成:${NC}" - command -v lazygit - lazygit --version + echo -e "${GREEN}lazygit 安装完成:${NC}" + command -v lazygit + lazygit --version } main diff --git a/script/mihomo-install.sh b/script/mihomo-install.sh index acb3769..bdefc57 100644 --- a/script/mihomo-install.sh +++ b/script/mihomo-install.sh @@ -1,15 +1,34 @@ #!/bin/bash -source $HOME/.dotfile/script/common/os-arch.sh +# 下载并安装 mihomo 代理内核到 /usr/local/bin -# 检查并安装 gunzip -function check_gunzip() { - if ! command -v gunzip &> /dev/null; then +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: mihomo-install + +根据当前 OS/架构从镜像下载 mihomo 二进制,安装到 /usr/local/bin/mihomo。 +支持 darwin/linux 的 amd64/arm64。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +source "$SCRIPT_DIR/common/os-arch.sh" + +check_gunzip() { + if ! command -v gunzip &>/dev/null; then echo "gunzip 未安装,正在安装..." - if command -v apt-get &> /dev/null; then + if command -v apt-get &>/dev/null; then sudo apt-get update && sudo apt-get install -y gzip - elif command -v yum &> /dev/null; then + elif command -v yum &>/dev/null; then sudo yum install -y gzip - elif command -v brew &> /dev/null; then + elif command -v brew &>/dev/null; then brew install gzip else echo "错误: 无法安装 gunzip,请手动安装 gzip 工具" @@ -18,57 +37,52 @@ function check_gunzip() { fi } -function install_mihomo() { - local os_arch=$(get_os_arch) +install_mihomo() { + local os_arch + os_arch=$(get_os_arch) local base_url="https://oss-default.yumee.top/bin/mihomo" local url="" - + case $os_arch in - "darwin-amd64"|"darwin-arm64"|"linux-amd64"|"linux-arm64") - url="${base_url}/mihomo-${os_arch}.gz" - echo "正在为 ${os_arch} 平台安装 mihomo..." - ;; - *) - echo "错误: 不支持的平台 ${os_arch}" - exit 1 - ;; + "darwin-amd64" | "darwin-arm64" | "linux-amd64" | "linux-arm64") + url="${base_url}/mihomo-${os_arch}.gz" + echo "正在为 ${os_arch} 平台安装 mihomo..." + ;; + *) + echo "错误: 不支持的平台 ${os_arch}" + exit 1 + ;; esac - - # 检查 gunzip 命令 + check_gunzip - - # 创建临时目录 - local temp_dir=$(mktemp -d) + + local temp_dir + temp_dir=$(mktemp -d) local temp_file="${temp_dir}/mihomo.gz" - - # 下载文件 + echo "正在从 ${url} 下载..." if ! curl -L -o "${temp_file}" "${url}"; then echo "错误: 下载失败" rm -rf "${temp_dir}" exit 1 fi - - # 解压文件 + echo "正在解压..." if ! gunzip "${temp_file}"; then echo "错误: 解压失败" rm -rf "${temp_dir}" exit 1 fi - - # 移动到 /usr/local/bin 并设置执行权限 + local binary="${temp_dir}/mihomo" echo "正在安装到 /usr/local/bin/mihomo..." sudo mv "${binary}" /usr/local/bin/mihomo sudo chmod +x /usr/local/bin/mihomo - - # 清理临时文件 + rm -rf "${temp_dir}" - + echo "mihomo 安装完成!" - echo "版本信息:" /usr/local/bin/mihomo version || echo "mihomo 已安装到 /usr/local/bin/mihomo" } -install_mihomo \ No newline at end of file +install_mihomo diff --git a/script/mise-install.sh b/script/mise-install.sh index e529f65..25b538f 100755 --- a/script/mise-install.sh +++ b/script/mise-install.sh @@ -1,41 +1,55 @@ #!/bin/bash +# 安装 mise 并链接 dotfile 配置、安装 config.toml 中的工具 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: mise-install + +1. 若未安装则通过官方脚本安装 mise +2. 链接 ~/.dotfile/mise/config.toml +3. 执行 mise install 安装配置中的工具 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e -# 设置颜色 GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' -NC='\033[0m' # No Color +NC='\033[0m' -# 检查是否已安装 mise -if ! command -v mise &> /dev/null -then +if ! command -v mise &>/dev/null; then echo -e "${YELLOW}mise 未安装. 正在安装 mise...${NC}" - # 安装 mise(官方安装脚本会自动配置 shell) curl https://mise.run | sh - - # 重新加载当前 shell 环境 export PATH="$HOME/.local/bin:$PATH" - - # 如果安装脚本没有自动配置,手动配置 fish shell(如果存在) + if [ -f ~/.config/fish/config.fish ]; then if ! grep -q "mise activate" ~/.config/fish/config.fish; then echo -e "${YELLOW}正在为 fish shell 配置 mise...${NC}" - echo '' >> ~/.config/fish/config.fish - echo '# mise (formerly rtx)' >> ~/.config/fish/config.fish - echo 'mise activate fish | source' >> ~/.config/fish/config.fish + { + echo '' + echo '# mise (formerly rtx)' + echo 'mise activate fish | source' + } >>~/.config/fish/config.fish echo -e "${GREEN}已将 mise 添加到 ~/.config/fish/config.fish${NC}" fi fi - + echo -e "${GREEN}mise 安装完成${NC}" else echo -e "${GREEN}mise 已安装${NC}" mise --version fi -# 链接 dotfile 中的 mise 配置 MISE_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mise" DOTFILE_MISE_CONFIG="$HOME/.dotfile/mise/config.toml" mkdir -p "$MISE_CONFIG_DIR" @@ -49,22 +63,15 @@ if [ -f "$DOTFILE_MISE_CONFIG" ]; then mise trust "$DOTFILE_MISE_CONFIG" 2>/dev/null || true fi -# 安装配置中的工具 -if command -v mise &> /dev/null && [ -f "$MISE_CONFIG_DIR/config.toml" ]; then +if command -v mise &>/dev/null && [ -f "$MISE_CONFIG_DIR/config.toml" ]; then echo -e "${YELLOW}正在安装 mise 工具...${NC}" mise install echo -e "${GREEN}mise 工具安装完成${NC}" fi -# 验证安装 -if command -v mise &> /dev/null -then +if command -v mise &>/dev/null; then echo -e "${GREEN}mise 安装成功!${NC}" - echo -e "${YELLOW}提示: 如果 mise 命令不可用,请重新打开终端或运行:${NC}" - echo -e "${GREEN}export PATH=\"\$HOME/.local/bin:\$PATH\"${NC}" - echo -e "${YELLOW}或者重新加载 shell 配置文件${NC}" else echo -e "${RED}mise 安装失败,请检查错误信息${NC}" exit 1 fi - diff --git a/script/nosudo.sh b/script/nosudo.sh index a46b6c3..030f882 100755 --- a/script/nosudo.sh +++ b/script/nosudo.sh @@ -1,7 +1,9 @@ #!/bin/bash # 为指定用户配置某命令的免密 sudo(写入 /etc/sudoers.d/) -# 用法: sudo nosudo -# 示例: sudo nosudo xuser docker + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" set -euo pipefail @@ -21,12 +23,14 @@ usage() { - 仅允许免密执行解析到的那个可执行文件路径(含子命令参数仍受 sudo 规则约束) - 对 docker,更推荐: sudo docker-group (无需 sudo 即可用 docker) - 删除规则: sudo rm /etc/sudoers.d/nosudo-- + +选项: + -h, --help 显示此帮助 EOF - exit 1 } -[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage -[[ $# -eq 2 ]] || usage +dotfile_help_requested "${1:-}" && dotfile_show_help +[[ $# -eq 2 ]] || { usage; exit 1; } if [[ $EUID -ne 0 ]]; then echo "错误: 请使用 sudo 运行,例如: sudo $0 $*" diff --git a/script/nvim-init.sh b/script/nvim-init.sh index 470ad03..2b91650 100644 --- a/script/nvim-init.sh +++ b/script/nvim-init.sh @@ -1,38 +1,49 @@ #!/bin/bash +# 将 Neovim 配置目录链接到 dotfile/nvim + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: nvim-init + +将 ~/.config/nvim 软链接到 ~/.dotfile/nvim。 +已有配置会备份为 nvim.backup.<时间戳>。 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help -# 定义变量 NVIM_CONFIG_DIR="$HOME/.config/nvim" -YOUR_CUSTOM_CONFIG_DIR="$HOME/.dotfile/nvim" # 请替换为您的自定义配置目录路径 +YOUR_CUSTOM_CONFIG_DIR="$HOME/.dotfile/nvim" -# 检查软链接是否已经存在并指向正确的目录 if [ -L "$NVIM_CONFIG_DIR" ] && [ "$(readlink -f "$NVIM_CONFIG_DIR")" = "$(readlink -f "$YOUR_CUSTOM_CONFIG_DIR")" ]; then echo "软链接已经存在并指向正确的目录。无需进行任何操作。" exit 0 fi -# 检查是否存在现有的 Neovim 配置 if [ -e "$NVIM_CONFIG_DIR" ]; then echo "检测到现有的 Neovim 配置。" - - # 如果是目录或常规文件,创建备份 if [ -d "$NVIM_CONFIG_DIR" ] || [ -f "$NVIM_CONFIG_DIR" ]; then BACKUP_DIR="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d%H%M%S)" mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR" echo "已将现有配置备份到 $BACKUP_DIR" else - # 如果是其他类型的文件(如损坏的软链接),直接删除 rm "$NVIM_CONFIG_DIR" echo "已删除现有的无效配置。" fi fi -# 创建软链接 ln -s "$YOUR_CUSTOM_CONFIG_DIR" "$NVIM_CONFIG_DIR" -# 检查软链接是否创建成功 if [ -L "$NVIM_CONFIG_DIR" ] && [ "$(readlink -f "$NVIM_CONFIG_DIR")" = "$(readlink -f "$YOUR_CUSTOM_CONFIG_DIR")" ]; then echo "软链接创建成功。Neovim 现在将使用您的自定义配置。" else echo "创建软链接时出错。请检查路径和权限。" + exit 1 fi - diff --git a/script/nvim-install.sh b/script/nvim-install.sh index df836dc..c971028 100755 --- a/script/nvim-install.sh +++ b/script/nvim-install.sh @@ -1,8 +1,28 @@ #!/bin/bash +# 从 GitHub Release 安装 Neovim 到 /usr/local/nvim + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: nvim-install + +按当前平台下载 Neovim v0.12.2 预编译包,安装到 /usr/local/nvim, +并链接 nvim/vim 到 /usr/local/bin。 + +支持: macOS x86_64/arm64, Linux x86_64/arm64 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e -# install neovim os_arch=$(uname -s)-$(uname -m) ver=v0.12.2 @@ -10,57 +30,49 @@ case $os_arch in "Darwin-x86_64") cd /tmp rm -rf nvim-macos-x86_64.tar.gz - wget https://github.com/neovim/neovim/releases/download/$ver/nvim-macos-x86_64.tar.gz + wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-macos-x86_64.tar.gz" tar zxf nvim-macos-x86_64.tar.gz - sudo mv -f /usr/local/nvim /usr/local/nvim-bak + sudo mv -f /usr/local/nvim /usr/local/nvim-bak 2>/dev/null || true sudo mv nvim-macos-x86_64 /usr/local/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/vim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim - echo "" vim -v ;; "Darwin-arm64" | "Darwin-aarch64") cd /tmp rm -rf nvim-macos-arm64.tar.gz - wget https://github.com/neovim/neovim/releases/download/$ver/nvim-macos-arm64.tar.gz + wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-macos-arm64.tar.gz" tar zxf nvim-macos-arm64.tar.gz [ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak sudo mv nvim-macos-arm64 /usr/local/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/vim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim - echo "" vim -v ;; "Linux-x86_64") cd /tmp - rm -rf nvim-linux64.tar.gz - wget https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-x86_64.tar.gz + rm -rf nvim-linux-x86_64.tar.gz + wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-x86_64.tar.gz" tar zxf nvim-linux-x86_64.tar.gz [ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak sudo mv nvim-linux-x86_64 /usr/local/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/vim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim - echo "" vim -v ;; "Linux-arm64" | "Linux-aarch64") cd /tmp - rm -rf nvim-linux64.tar.gz - wget https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-arm64.tar.gz - tar zxf nvim-linux64.tar.gz + rm -rf nvim-linux-arm64.tar.gz + wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-arm64.tar.gz" + tar zxf nvim-linux-arm64.tar.gz [ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak - sudo mv nvim-linux64 /usr/local/nvim + sudo mv nvim-linux-arm64 /usr/local/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/vim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim - echo "" vim -v ;; - *) echo "不支持的平台: $os_arch" >&2 exit 1 ;; esac - -# if has error "no c compile found" -# sudo apt install build-essential diff --git a/script/nvm-install.sh b/script/nvm-install.sh index 4bf7cd0..a35181e 100644 --- a/script/nvm-install.sh +++ b/script/nvm-install.sh @@ -1,9 +1,31 @@ #!/bin/bash +# 安装 fnm(Node 版本管理)并 reload fish 配置 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: nvm-install + +通过 fnm 官方安装脚本安装 Fast Node Manager,并执行 ff reload。 + +选项: + -h, --help 显示此帮助 + +说明: 本仓库 fish 配置中 nvm 别名指向 fnm。 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail -# https://github.com/Schniz/fnm curl -fsSL https://fnm.vercel.app/install | bash -ff reload - -# after install node -# corepack enable pnpm \ No newline at end of file +if command -v ff &>/dev/null; then + ff reload +elif command -v fish &>/dev/null; then + fish -c "source ~/.config/fish/config.fish" +fi diff --git a/script/python-install.sh b/script/python-install.sh index e0e6a4b..2a312b5 100644 --- a/script/python-install.sh +++ b/script/python-install.sh @@ -1,38 +1,51 @@ #!/bin/bash +# 安装 pyenv 并配置 Python 3.11.10 及常用包 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: python-install + +1. 若未安装则安装 pyenv +2. 安装 Python 3.11.10 并设为 global +3. 升级 pip 并安装 ipython/jupyter 等常用包 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help set -e -# 设置颜色 GREEN='\033[0;32m' RED='\033[0;31m' -NC='\033[0m' # No Color +NC='\033[0m' -# 检查是否已安装 pyenv -if ! command -v pyenv &> /dev/null -then +if ! command -v pyenv &>/dev/null; then echo -e "${RED}pyenv 未安装. 正在安装 pyenv...${NC}" - # 安装 pyenv curl https://pyenv.run | bash - source ~/.config/fish/config.fish + if [ -f ~/.config/fish/config.fish ]; then + # shellcheck disable=SC1090 + source ~/.config/fish/config.fish 2>/dev/null || true + fi else echo -e "${GREEN}pyenv 已安装${NC}" fi -# 询问用户想安装的 Python 版本 python_version=3.11.10 -# 安装指定版本的 Python echo -e "${GREEN}正在安装 Python $python_version...${NC}" -pyenv install $python_version +pyenv install "$python_version" +pyenv global "$python_version" -# 设置全局 Python 版本 -pyenv global $python_version - -# 验证安装 installed_version=$(python --version) echo -e "${GREEN}Python 安装完成. 当前版本: $installed_version${NC}" -# 安装一些常用的 Python 包 echo -e "${GREEN}正在安装一些常用的 Python 包...${NC}" pip install --upgrade pip pip install ipython jupyter notebook pandas numpy matplotlib scikit-learn diff --git a/script/scan-ip.sh b/script/scan-ip.sh index dd4c559..684813b 100755 --- a/script/scan-ip.sh +++ b/script/scan-ip.sh @@ -1,12 +1,29 @@ #!/bin/bash +# ICMP/TCP 扫描本网段或指定网段的在线主机与开放端口 -# 用法: -# ./scan-ip.sh # ICMP 扫描本网段在线 IP -# ./scan-ip.sh 10.0.0 # ICMP 扫描指定网段(10.0.0.1-254) -# ./scan-ip.sh 22 # 扫描本网段中 22 端口开放的 IP -# ./scan-ip.sh 80,443,8080 # 扫描本网段中任一指定端口开放的 IP -# ./scan-ip.sh 22 10.0.0 # 指定网段前缀 + 端口(顺序不限) -# ./scan-ip.sh 10.0.0 22 # 同上 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: scan-ip [端口或网段参数...] + +扫描局域网主机: + 无参数 ICMP 扫描本网段 .1-.254 + 10.0.0 扫描指定网段前缀 + 22 扫描本网段 TCP 22 端口 + 80,443,8080 扫描多个端口(任一开放即显示) + 22 10.0.0 指定网段 + 端口(顺序不限) + +选项: + -h, --help 显示此帮助 + +依赖: ping;端口模式需要 nc (netcat)。 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help TIMEOUT=1 @@ -31,8 +48,7 @@ fi if [ -n "$1" ] && [ -z "$PORT_SPEC" ] && [ -z "$PREFIX_ARG" ]; then echo "错误: 无法识别参数: $1" - echo "端口支持: 单端口(22) / 列表(80,443) / 范围(8000-8100)" - echo "网段支持: 前缀(10.0.0 或 192.168.1.0)" + usage exit 1 fi @@ -52,7 +68,6 @@ else PREFIX="${LOCAL_IP%.*}" fi -# 解析端口参数 -> PORTS 数组 PORTS=() if [ -n "$PORT_SPEC" ]; then if [[ "$PORT_SPEC" =~ ^[0-9]+$ ]]; then @@ -68,7 +83,6 @@ if [ -n "$PORT_SPEC" ]; then IFS=',' read -ra PORTS <<<"$PORT_SPEC" else echo "错误: 无效的端口格式: $PORT_SPEC" - echo "支持: 单端口(22) / 列表(80,443) / 范围(8000-8100)" exit 1 fi fi @@ -81,10 +95,7 @@ else fi echo "------------------------------------------------------" -# ---------- 模式一:ICMP 在线扫描(无端口参数) ---------- if [ ${#PORTS[@]} -eq 0 ]; then - # macOS: -W 单位毫秒,-S 绑定源 IP - # Linux: -W 单位秒, -I 绑定源 IP if [ "$(uname -s)" = "Darwin" ]; then for i in $(seq 1 254); do ping -c 1 -W 1000 -S "$LOCAL_IP" "$PREFIX.$i" >/dev/null 2>&1 && echo "$PREFIX.$i 在线" & @@ -100,15 +111,11 @@ if [ ${#PORTS[@]} -eq 0 ]; then exit 0 fi -# ---------- 模式二:TCP 端口扫描 ---------- if ! command -v nc &>/dev/null; then - echo "错误: 找不到 nc (netcat),请先安装:" - echo " macOS: brew install netcat" - echo " Debian/Ubuntu: sudo apt-get install netcat-openbsd" + echo "错误: 找不到 nc (netcat)" exit 1 fi -# nc 在不同实现里 -z/-w 行为基本一致;用 -G 在 macOS 上控制连接超时更准 NC_OPTS=(-z -w "$TIMEOUT") if [ "$(uname -s)" = "Darwin" ]; then NC_OPTS+=(-G "$TIMEOUT") diff --git a/script/scan-port.sh b/script/scan-port.sh index aa02d04..875122a 100755 --- a/script/scan-port.sh +++ b/script/scan-port.sh @@ -1,25 +1,42 @@ #!/bin/bash +# 对指定 IP 进行 TCP 端口扫描 -# 端口扫描脚本 -# 用法: ./scan-port.sh [端口范围] -# 示例: ./scan-port.sh 192.168.1.1 -# 示例: ./scan-port.sh 192.168.1.1 80,443,8080 -# 示例: ./scan-port.sh 192.168.1.1 1-1000 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: scan-port [端口范围] + +扫描目标 IP 的 TCP 端口(使用 nc)。 + +参数: + IP 目标 IPv4 地址(必填) + 端口范围 可选。默认扫描常用端口列表。 + 支持: 80,443,8080 或 1-1000 + +选项: + -h, --help 显示此帮助 + +示例: + scan-port 192.168.1.1 + scan-port 192.168.1.1 22,80,443 + scan-port 192.168.1.1 1-1000 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help -# 检查是否提供了 IP 地址参数 if [ -z "$1" ]; then - echo "错误: 请提供要扫描的 IP 地址" - echo "用法: $0 [端口范围]" - echo "示例: $0 192.168.1.1" - echo "示例: $0 192.168.1.1 80,443,8080" - echo "示例: $0 192.168.1.1 1-1000" + echo "错误: 请提供要扫描的 IP 地址" >&2 + usage exit 1 fi TARGET_IP="$1" -TIMEOUT=1 # 连接超时时间(秒) +TIMEOUT=1 -# 验证 IP 地址格式(简单验证) if ! [[ "$TARGET_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then echo "错误: 无效的 IP 地址格式: $TARGET_IP" exit 1 @@ -29,45 +46,33 @@ echo "==========================================" echo "正在扫描 IP: $TARGET_IP" echo "==========================================" -# 检查是否有 nc (netcat) 命令 if ! command -v nc &>/dev/null; then echo "错误: 找不到 nc (netcat) 命令。" - echo "请安装 netcat:" - echo " macOS: brew install netcat" - echo " Linux: sudo apt-get install netcat 或 sudo yum install nc" exit 1 fi -# 确定要扫描的端口 if [ -z "$2" ]; then - # 如果没有指定端口,扫描常用端口 PORTS=(21 22 23 25 53 80 110 111 135 139 143 443 445 993 995 1723 3306 3389 5432 5900 8080 8443) echo "使用默认常用端口列表" else - # 解析端口参数 PORT_SPEC="$2" - - # 检查是否是范围格式 (例如: 1-1000) + if [[ "$PORT_SPEC" =~ ^[0-9]+-[0-9]+$ ]]; then START_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f1) END_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f2) - + if [ "$START_PORT" -gt "$END_PORT" ]; then echo "错误: 起始端口 ($START_PORT) 不能大于结束端口 ($END_PORT)" exit 1 fi - + PORTS=($(seq "$START_PORT" "$END_PORT")) - echo "扫描端口范围: $START_PORT-$END_PORT (共 $((END_PORT - START_PORT + 1)) 个端口)" - # 检查是否是逗号分隔的端口列表 (例如: 80,443,8080) + echo "扫描端口范围: $START_PORT-$END_PORT" elif [[ "$PORT_SPEC" =~ ^[0-9,]+$ ]]; then - IFS=',' read -ra PORTS <<< "$PORT_SPEC" + IFS=',' read -ra PORTS <<<"$PORT_SPEC" echo "扫描指定端口: $PORT_SPEC" else echo "错误: 无效的端口格式: $PORT_SPEC" - echo "支持的格式:" - echo " - 逗号分隔: 80,443,8080" - echo " - 范围: 1-1000" exit 1 fi fi @@ -75,20 +80,14 @@ fi echo "超时设置: ${TIMEOUT}秒" echo "------------------------------------------------------" -# 统计变量 OPEN_PORTS=() CLOSED_COUNT=0 TOTAL_PORTS=${#PORTS[@]} -# 扫描端口函数 scan_port() { local port=$1 local ip=$2 - - # 使用 nc 扫描端口 - # -z: 只扫描,不发送数据 - # -v: 详细输出 - # -w: 超时时间 + if nc -z -v -w "$TIMEOUT" "$ip" "$port" 2>&1 | grep -q "succeeded\|open"; then echo "✅ 端口 $port: 开放" return 0 @@ -97,7 +96,6 @@ scan_port() { fi } -# 并行扫描端口(使用后台任务) for port in "${PORTS[@]}"; do if scan_port "$port" "$TARGET_IP"; then OPEN_PORTS+=("$port") @@ -106,7 +104,6 @@ for port in "${PORTS[@]}"; do fi done -# 等待所有后台任务完成 wait echo "------------------------------------------------------" @@ -128,4 +125,3 @@ else fi echo "==========================================" - diff --git a/script/tmux-init.sh b/script/tmux-init.sh index 616601b..085c178 100755 --- a/script/tmux-init.sh +++ b/script/tmux-init.sh @@ -1,20 +1,41 @@ -#/bin/bash +#!/bin/bash +# 链接 tmux 配置并安装 TPM 插件 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: tmux-init + +1. 将 ~/.tmux.conf 链接到 ~/.dotfile/tmux/.tmux.conf +2. 克隆 tpm 到 ~/.tmux/plugins/tpm(若不存在) +3. 安装插件并 reload tmux 配置 + +选项: + -h, --help 显示此帮助 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + +set -euo pipefail echo 'Init tmux config' src=$HOME/.tmux.conf -if [ -e $src ]; then - mv $src $src.bak +if [ -e "$src" ]; then + mv "$src" "$src.bak" fi -ln -s $HOME/.dotfile/tmux/.tmux.conf $src - +ln -s "$HOME/.dotfile/tmux/.tmux.conf" "$src" echo 'Install tpm' if [ ! -e ~/.tmux/plugins/tpm ]; then - git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm + git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm fi echo 'Install tmux plugin by tpm' -$HOME/.tmux/plugins/tpm/bin/install_plugins +"$HOME/.tmux/plugins/tpm/bin/install_plugins" echo 'Reload tmux' -tmux source ~/.tmux.conf \ No newline at end of file +tmux source ~/.tmux.conf diff --git a/script/toggle-region.sh b/script/toggle-region.sh index 3f1b0a0..9fe5eea 100644 --- a/script/toggle-region.sh +++ b/script/toggle-region.sh @@ -1,6 +1,28 @@ #!/bin/bash -# 切换 CN_REGION 环境变量 -# cn ↔ intl 切换 +# 切换 CN_REGION 环境变量(国内/国外镜像与安装源) + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: toggle-region + +在当前 shell 中切换 CN_REGION: + 1 -> 0 国内 -> 国外 + 0/未设置 -> 1 国外 -> 国内 + +影响依赖 CN_REGION 的安装脚本(如 docker-install)。 + +选项: + -h, --help 显示此帮助 + +说明: 仅影响当前 shell 会话;持久化请写入 fish/zsh 配置。 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help if [ "$CN_REGION" = "1" ]; then export CN_REGION=0 @@ -9,4 +31,3 @@ else export CN_REGION=1 echo "[INFO] Region switched to: 1 (国内)" fi - diff --git a/script/zsh-init.sh b/script/zsh-init.sh index 25ba0ec..320b073 100755 --- a/script/zsh-init.sh +++ b/script/zsh-init.sh @@ -1,4 +1,27 @@ #!/bin/bash +# 链接 zshrc、安装 Oh My Zsh,并将默认 shell 设为 zsh + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common/help.sh" + +usage() { + cat <<'EOF' +用法: zsh-init + +1. 将 ~/.zshrc 链接到 ~/.dotfile/zsh/.zshrc +2. 若未安装则安装 Oh My Zsh(--unattended --keep-zshrc) +3. 将默认登录 shell 设为 zsh + +选项: + -h, --help 显示此帮助 + +本机额外配置可写入 ~/.zshrc.local。 +EOF +} + +dotfile_help_requested "${1:-}" && dotfile_show_help + set -euo pipefail DOTFILE="${DOTFILE:-$HOME/.dotfile}" @@ -7,33 +30,33 @@ ZSHRC_HOME="$HOME/.zshrc" echo "Link zshrc" if [[ -f "$ZSHRC_HOME" && ! -L "$ZSHRC_HOME" ]]; then - mv "$ZSHRC_HOME" "$ZSHRC_HOME.bak" - echo " 已备份原文件 -> $ZSHRC_HOME.bak" + mv "$ZSHRC_HOME" "$ZSHRC_HOME.bak" + echo " 已备份原文件 -> $ZSHRC_HOME.bak" fi ln -sf "$ZSHRC_REPO" "$ZSHRC_HOME" echo " $ZSHRC_HOME -> $ZSHRC_REPO" if [[ ! -d "$HOME/.oh-my-zsh" ]]; then - echo "安装 Oh My Zsh(--unattended:安装脚本内不改 shell、不自动进 zsh;--keep-zshrc:保留已链接的 .zshrc)…" - sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended --keep-zshrc + echo "安装 Oh My Zsh…" + sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended --keep-zshrc else - echo "Oh My Zsh 已存在,跳过安装 (~/.oh-my-zsh)" + echo "Oh My Zsh 已存在,跳过安装 (~/.oh-my-zsh)" fi echo "" echo "将默认登录 shell 设为 zsh" zshpath="$(command -v zsh)" if [[ -z "$zshpath" || ! -x "$zshpath" ]]; then - echo "错误: 找不到可执行的 zsh,请先安装 zsh。" >&2 - exit 1 + echo "错误: 找不到可执行的 zsh,请先安装 zsh。" >&2 + exit 1 fi if grep -qxF "$zshpath" /etc/shells 2>/dev/null; then - echo " $zshpath 已在 /etc/shells 中" + echo " $zshpath 已在 /etc/shells 中" else - echo "$zshpath" | sudo tee -a /etc/shells + echo "$zshpath" | sudo tee -a /etc/shells fi chsh -s "$zshpath" echo "" -echo "完成。当前仓库的 .zshrc 已启用 z 插件(Oh My Zsh plugins/z),默认 shell 已改为: $zshpath" -echo "请重新登录或新开终端后生效。本机额外配置可写入 ~/.zshrc.local(不会被链接覆盖)。" +echo "完成。默认 shell 已改为: $zshpath" +echo "请重新登录或新开终端后生效。"