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 <cursoragent@cursor.com>
This commit is contained in:
2026-06-25 15:09:35 +08:00
parent 1f6b042b26
commit 67291a9a50
36 changed files with 1056 additions and 442 deletions
+14 -12
View File
@@ -65,22 +65,24 @@ complete -c ff -n "__fish_seen_subcommand_from ssh copy; and test (count (comman
# script 子命令的补全 - 动态列出 script 目录下的脚本 # script 子命令的补全 - 动态列出 script 目录下的脚本
function __ff_list_scripts function __ff_list_scripts
set -l script_dir ~/.dotfile/script if not functions -q script-list-names
if test -d $script_dir source ~/.dotfile/fish/functions/script-catalog.fish
# 列出 .sh 文件
for script in $script_dir/*.sh
if test -f $script
basename $script | string replace -r '\.sh$' ''
end end
script-list-names
end end
# 列出 .py 文件
for script in $script_dir/*.py function __ff_complete_scripts
if test -f $script if not functions -q script-extract-description
basename $script | string replace -r '\.py$' '' source ~/.dotfile/fish/functions/script-catalog.fish
end
end 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
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"
+22 -45
View File
@@ -1,65 +1,42 @@
# Execute script from script directory # Execute script from script directory
function execute-script 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 dotfile_dir ~/.dotfile
set -l script_dir $dotfile_dir/script set -l script_dir $dotfile_dir/script
set -l script_name $argv[1] set -l script_name $argv[1]
# 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" if test -z "$script_name"
echo "Error: No script name specified" script-show-catalog
echo "Usage: execute-script <script_name> [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
end
return 1 return 1
end end
# 尝试不同的扩展名 set -l script_path (script-resolve-path $script_name 2>/dev/null; or echo "")
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 -n "$script_path" -a -f "$script_path"
if test -z "$script_path"
set script_path $script_dir/$script_name
end
if test -f $script_path
echo "Running: $script_path" echo "Running: $script_path"
if test -x $script_path if test -x $script_path
$script_path $argv[2..-1] $script_path $argv[2..-1]
else else if string match -q "*.py" $script_path
# 根据文件类型选择解释器
if string match -q "*.py" $script_path
python3 $script_path $argv[2..-1] python3 $script_path $argv[2..-1]
else else
bash $script_path $argv[2..-1] bash $script_path $argv[2..-1]
end end
end return $status
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
end
end end
echo "Error: Script not found: $script_name"
echo ""
script-show-catalog
return 1
end
+126
View File
@@ -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 <name> [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 <name> -h 查看单个脚本内的详细帮助"
end
+27
View File
@@ -1 +1,28 @@
#!/bin/bash #!/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
+22
View File
@@ -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_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_CORE_GIT_REMOTE="https://mirrors.ustc.edu.cn/homebrew-core.git"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.ustc.edu.cn/homebrew-bottles" export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.ustc.edu.cn/homebrew-bottles"
+27 -1
View File
@@ -1,5 +1,31 @@
#!/usr/bin/env bash #!/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 set -euo pipefail
+11
View File
@@ -0,0 +1,11 @@
# 供 script/*.sh source:统一处理 -h / --help
# 调用方需先定义 usage() 函数
dotfile_help_requested() {
[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]
}
dotfile_show_help() {
usage
exit 0
}
+25 -9
View File
@@ -1,21 +1,40 @@
#!/bin/bash #!/bin/bash
# 执行命令并在失败时通过飞书 Webhook 发送通知
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: croncheck <command> [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 if [ "$#" -lt 1 ]; then
echo "Usage: $0 <command> [command_args...]" echo "错误: 缺少要执行的命令" >&2
usage
exit 1 exit 1
fi fi
# 获取 webhook URL
WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/808eafd8-a79f-4af0-b730-8442cf15933b" WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/808eafd8-a79f-4af0-b730-8442cf15933b"
# 执行命令并捕获输出
OUTPUT=$("$@" 2>&1) OUTPUT=$("$@" 2>&1)
EXIT_CODE=$? EXIT_CODE=$?
# 如果命令失败,发送 webhook 通知
if [ $EXIT_CODE -ne 0 ]; then if [ $EXIT_CODE -ne 0 ]; then
# 准备 JSON 数据
JSON_DATA=$(cat <<EOF JSON_DATA=$(cat <<EOF
{ {
"msg_type": "text", "msg_type": "text",
@@ -25,10 +44,7 @@ if [ $EXIT_CODE -ne 0 ]; then
} }
EOF EOF
) )
# 发送 webhook
curl -s -X POST -H "Content-Type: application/json" -d "$JSON_DATA" "$WEBHOOK_URL" curl -s -X POST -H "Content-Type: application/json" -d "$JSON_DATA" "$WEBHOOK_URL"
fi fi
# 退出,使用原命令的退出码
exit $EXIT_CODE exit $EXIT_CODE
+23 -2
View File
@@ -1,15 +1,36 @@
#!/bin/bash #!/bin/bash
# 将 Cursor 用户配置链接到 dotfile(macOS)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: cursor-init
链接 ~/.dotfile/cursor/config/ 下的 settings.json、keybindings.json
到 Cursor 用户目录(macOS)。
已有文件会备份为 *.bak。
选项:
-h, --help 显示此帮助
EOF
}
dotfile_help_requested "${1:-}" && dotfile_show_help
set -euo pipefail
echo 'Cursor config init' echo 'Cursor config init'
# init settings.json
src="$HOME/Library/Application Support/Cursor/User/settings.json" src="$HOME/Library/Application Support/Cursor/User/settings.json"
if [ -f "$src" ]; then if [ -f "$src" ]; then
mv "$src" "$src.bak" mv "$src" "$src.bak"
fi fi
ln -s "$HOME/.dotfile/cursor/config/settings.json" "$src" ln -s "$HOME/.dotfile/cursor/config/settings.json" "$src"
# init keybindings.json
src="$HOME/Library/Application Support/Cursor/User/keybindings.json" src="$HOME/Library/Application Support/Cursor/User/keybindings.json"
if [ -f "$src" ]; then if [ -f "$src" ]; then
mv "$src" "$src.bak" mv "$src" "$src.bak"
+19 -1
View File
@@ -1,8 +1,26 @@
#!/bin/bash #!/bin/bash
# Debian 国内镜像源更换脚本 # Debian 国内镜像源更换脚本(交互式选择镜像)
# 支持 Debian 9/10/11/12/13 (stretch/buster/bullseye/bookworm/trixie) # 支持 Debian 9/10/11/12/13 (stretch/buster/bullseye/bookworm/trixie)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: sudo debian-init
交互式将 Debian apt 源更换为国内镜像(需 root)。
自动检测版本代号,备份原 sources.list。
选项:
-h, --help 显示此帮助
EOF
}
dotfile_help_requested "${1:-}" && dotfile_show_help
set -e set -e
# 颜色定义 # 颜色定义
+18
View File
@@ -3,6 +3,24 @@
# Debian 一键更换清华镜像源脚本 (DEB822 格式) # Debian 一键更换清华镜像源脚本 (DEB822 格式)
# 支持 Debian 12 (bookworm)、13 (trixie)、14 (forky) 及后续版本 # 支持 Debian 12 (bookworm)、13 (trixie)、14 (forky) 及后续版本
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: sudo debian-tuna
将 Debian apt 源一键替换为清华 DEB822 格式源(需 root)。
会处理 /etc/apt/sources.list.d/debian.sources 与旧版 sources.list。
选项:
-h, --help 显示此帮助
EOF
}
dotfile_help_requested "${1:-}" && dotfile_show_help
set -e set -e
# 颜色定义 # 颜色定义
+1
View File
@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# 查询目录大小(du 封装,支持排序与 Top N)
set -eu set -eu
+21 -2
View File
@@ -1,7 +1,26 @@
#!/bin/bash #!/bin/bash
# 清理 macOS / Linux 系统 DNS 缓存
# 用法: SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ./dns-clean.sh # 清理 macOS / Linux 系统 DNS 缓存 # shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: dns-clean
按当前操作系统清理 DNS 缓存:
macOS: dscacheutil + mDNSResponder
Linux: systemd-resolved / nscd / dnsmasq / unbound(自动检测)
选项:
-h, --help 显示此帮助
说明: 非 root 时会自动使用 sudo。
EOF
}
dotfile_help_requested "${1:-}" && dotfile_show_help
set -e set -e
+23
View File
@@ -1,2 +1,25 @@
#!/bin/bash #!/bin/bash
# 清理 Docker 未使用的镜像、容器与缓存(docker system prune -a)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
usage() {
cat <<'EOF'
用法: docker-clean
执行 docker system prune -a,删除未使用的镜像、容器、网络等。
选项:
-h, --help 显示此帮助
警告: 会删除未使用的镜像,请确认无重要 dangling 资源。
EOF
}
dotfile_help_requested "${1:-}" && dotfile_show_help
set -euo pipefail
docker system prune -a docker system prune -a
+9 -5
View File
@@ -1,7 +1,9 @@
#!/bin/bash #!/bin/bash
# 将用户加入 docker 组(免 sudo 使用 docker) # 将用户加入 docker 组(免 sudo 使用 docker)
# 用法: sudo docker-group <user>
# 示例: sudo docker-group xuser SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
set -euo pipefail set -euo pipefail
@@ -17,12 +19,14 @@ usage() {
说明: 说明:
- 用户需重新登录或 newgrp docker 后生效 - 用户需重新登录或 newgrp docker 后生效
- 安装 Docker 时 docker-install.sh 只会把当时的 $USER 加入组 - 安装 Docker 时 docker-install.sh 只会把当时的 $USER 加入组
选项:
-h, --help 显示此帮助
EOF EOF
exit 1
} }
[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage dotfile_help_requested "${1:-}" && dotfile_show_help
[[ $# -eq 1 ]] || usage [[ $# -eq 1 ]] || { usage; exit 1; }
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
echo "错误: 请使用 sudo 运行,例如: sudo $0 $1" echo "错误: 请使用 sudo 运行,例如: sudo $0 $1"
+23 -5
View File
@@ -1,4 +1,27 @@
#!/bin/bash #!/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 set -e
@@ -38,8 +61,6 @@ install_prerequisites() {
sudo apt-get install -y ca-certificates curl gnupg lsb-release sudo apt-get install -y ca-certificates curl gnupg lsb-release
} }
#################################################################
# 国内安装(使用清华镜像)
install_docker_cn() { install_docker_cn() {
local repo_base="https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/${DISTRO}" local repo_base="https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/${DISTRO}"
local keyring="/usr/share/keyrings/docker-archive-keyring.gpg" local keyring="/usr/share/keyrings/docker-archive-keyring.gpg"
@@ -56,8 +77,6 @@ install_docker_cn() {
sudo usermod -aG docker "$USER" sudo usermod -aG docker "$USER"
} }
#################################################################
# 国外安装(使用官方源)
install_docker_intl() { install_docker_intl() {
local repo_base="https://download.docker.com/linux/${DISTRO}" local repo_base="https://download.docker.com/linux/${DISTRO}"
local keyring="/etc/apt/keyrings/docker.asc" local keyring="/etc/apt/keyrings/docker.asc"
@@ -81,7 +100,6 @@ detect_distro
remove_conflicting_packages remove_conflicting_packages
install_prerequisites install_prerequisites
# 根据环境变量选择安装方式(须在函数定义之后调用)
if [ "$CN_REGION" = "1" ]; then if [ "$CN_REGION" = "1" ]; then
echo "[INFO] 国内环境,使用清华镜像安装 Docker (${DISTRO}/${CODENAME})" echo "[INFO] 国内环境,使用清华镜像安装 Docker (${DISTRO}/${CODENAME})"
install_docker_cn install_docker_cn
+21
View File
@@ -1,4 +1,25 @@
#!/bin/bash #!/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/ cd ~/Downloads/
wget https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip wget https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip
+23 -11
View File
@@ -1,4 +1,25 @@
#!/bin/bash #!/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" LOCAL_CONFIG="$HOME/.config/fish/config.fish"
DEFAULT_PATH="$HOME/.dotfile/fish/default.fish" DEFAULT_PATH="$HOME/.dotfile/fish/default.fish"
@@ -8,7 +29,6 @@ mkdir -p "$HOME/.config/fish"
echo 'Setup fish config.fish wrapper' echo 'Setup fish config.fish wrapper'
# 旧版迁移:如果 config.fish 是指向 dotfile 的 symlink(老布局),移除
if [ -L "$LOCAL_CONFIG" ]; then if [ -L "$LOCAL_CONFIG" ]; then
link_target=$(readlink "$LOCAL_CONFIG") link_target=$(readlink "$LOCAL_CONFIG")
case "$link_target" in case "$link_target" in
@@ -19,9 +39,6 @@ if [ -L "$LOCAL_CONFIG" ]; then
esac esac
fi fi
# 不存在 -> 创建只含一行 source 的本地 wrapper
# 存在但未 source default.fish -> 在文件顶部插入 source 行
# 已 source -> 跳过
if [ ! -f "$LOCAL_CONFIG" ]; then if [ ! -f "$LOCAL_CONFIG" ]; then
echo "$SOURCE_LINE" >"$LOCAL_CONFIG" echo "$SOURCE_LINE" >"$LOCAL_CONFIG"
echo " Created $LOCAL_CONFIG (sources default.fish)" echo " Created $LOCAL_CONFIG (sources default.fish)"
@@ -34,17 +51,14 @@ else
echo " $LOCAL_CONFIG already sources default.fish, skip" echo " $LOCAL_CONFIG already sources default.fish, skip"
fi fi
# 确保目标目录存在
echo 'Link fish functions and completions' echo 'Link fish functions and completions'
mkdir -p "$HOME/.config/fish/functions" mkdir -p "$HOME/.config/fish/functions"
mkdir -p "$HOME/.config/fish/completions" mkdir -p "$HOME/.config/fish/completions"
# 链接 functions 目录下的所有文件
for file in $HOME/.dotfile/fish/functions/*.fish; do for file in $HOME/.dotfile/fish/functions/*.fish; do
if [ -f "$file" ]; then if [ -f "$file" ]; then
filename=$(basename "$file") filename=$(basename "$file")
target="$HOME/.config/fish/functions/$filename" target="$HOME/.config/fish/functions/$filename"
# 如果已存在且不是符号链接,备份
if [ -f "$target" ] && [ ! -L "$target" ]; then if [ -f "$target" ] && [ ! -L "$target" ]; then
mv "$target" "$target.bak" mv "$target" "$target.bak"
fi fi
@@ -53,12 +67,10 @@ for file in $HOME/.dotfile/fish/functions/*.fish; do
fi fi
done done
# 链接 completions 目录下的所有文件
for file in $HOME/.dotfile/fish/completions/*.fish; do for file in $HOME/.dotfile/fish/completions/*.fish; do
if [ -f "$file" ]; then if [ -f "$file" ]; then
filename=$(basename "$file") filename=$(basename "$file")
target="$HOME/.config/fish/completions/$filename" target="$HOME/.config/fish/completions/$filename"
# 如果已存在且不是符号链接,备份
if [ -f "$target" ] && [ ! -L "$target" ]; then if [ -f "$target" ] && [ ! -L "$target" ]; then
mv "$target" "$target.bak" mv "$target" "$target.bak"
fi fi
@@ -72,5 +84,5 @@ fish -c "fisher install jethrokuan/z"
echo 'Set fish as default shell' echo 'Set fish as default shell'
fishpath=$(which fish) fishpath=$(which fish)
echo $fishpath | sudo tee -a /etc/shells echo "$fishpath" | sudo tee -a /etc/shells
chsh -s $fishpath chsh -s "$fishpath"
+50 -24
View File
@@ -1,83 +1,109 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# 下载并安装 Sarasa Nerd / Fira Code 字体到用户字体目录
import argparse
import os import os
import sys
import platform import platform
import subprocess import subprocess
from typing import Dict, List, Optional import sys
import urllib.request import urllib.request
import zipfile import zipfile
import shutil 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: def get_font_dir() -> str:
"""根据操作系统确定字体安装目录"""
system = platform.system() system = platform.system()
if system == "Darwin": # macOS if system == "Darwin":
return os.path.expanduser("~/Library/Fonts") return os.path.expanduser("~/Library/Fonts")
elif system == "Linux": if system == "Linux":
return os.path.expanduser("~/.local/share/fonts") return os.path.expanduser("~/.local/share/fonts")
else:
raise OSError(f"不支持的操作系统: {system}") raise OSError(f"不支持的操作系统: {system}")
def check_command(command: str) -> bool: def check_command(command: str) -> bool:
"""检查命令是否存在"""
return shutil.which(command) is not None return shutil.which(command) is not None
def download_file(url: str, dest_path: str) -> None: def download_file(url: str, dest_path: str) -> None:
"""下载文件到指定路径"""
try: try:
urllib.request.urlretrieve(url, dest_path) urllib.request.urlretrieve(url, dest_path)
except Exception as e: except Exception as e:
print(f"下载失败: {e}") print(f"下载失败: {e}")
sys.exit(1) sys.exit(1)
def get_top_level_dirs(zip_file: zipfile.ZipFile) -> List[str]: 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: def has_single_top_dir(zip_file: zipfile.ZipFile) -> bool:
"""检查 ZIP 文件是否只有一个顶级目录"""
top_dirs = get_top_level_dirs(zip_file) 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: 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): if has_single_top_dir(zip_ref):
# ZIP 文件有单一顶级目录,直接解压
zip_ref.extractall("/tmp") zip_ref.extractall("/tmp")
print(f"已解压 {font_name} 到现有顶级目录") print(f"已解压 {font_name} 到现有顶级目录")
else: else:
# 没有单一顶级目录,创建一个以字体名命名的目录
font_dir = os.path.join("/tmp", font_name) font_dir = os.path.join("/tmp", font_name)
os.makedirs(font_dir, exist_ok=True) os.makedirs(font_dir, exist_ok=True)
for file in zip_ref.namelist(): for file in zip_ref.namelist():
if not file.startswith('__MACOSX'): # 排除 macOS 系统文件 if not file.startswith("__MACOSX"):
zip_ref.extract(file, font_dir) zip_ref.extract(file, font_dir)
print(f"已解压 {font_name} 并创建新的顶级目录") print(f"已解压 {font_name} 并创建新的顶级目录")
def install_font(font_name: str, font_dir: str) -> None: def install_font(font_name: str, font_dir: str) -> None:
"""安装字体到指定目录""" ttf_files = [
# find ttf files to copy to the font_dir 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: for ttf_file in ttf_files:
shutil.copy(f"/tmp/{font_name}/{ttf_file}", font_dir) shutil.copy(f"/tmp/{font_name}/{ttf_file}", font_dir)
print(f"已安装 {font_name} 到 {font_dir}") print(f"已安装 {font_name} 到 {font_dir}")
def refresh_font_cache() -> None: def refresh_font_cache() -> None:
"""在 Linux 系统上刷新字体缓存"""
if platform.system() == "Linux" and check_command("fc-cache"): if platform.system() == "Linux" and check_command("fc-cache"):
subprocess.run(["fc-cache", "-f"]) subprocess.run(["fc-cache", "-f"], check=False)
print("字体缓存已刷新。") print("字体缓存已刷新。")
else: else:
print("警告:无法刷新字体缓存。您可能需要手动刷新。") print("警告:无法刷新字体缓存。您可能需要手动刷新。")
def main() -> None: 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] = { 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", "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" "FiraCode": "https://github.com/tonsky/FiraCode/releases/download/6.2/Fira_Code_v6.2.zip",
} }
try: try:
@@ -99,6 +125,6 @@ def main() -> None:
refresh_font_cache() refresh_font_cache()
print("安装完成。请重启您的终端或应用程序以使用新字体。") print("安装完成。请重启您的终端或应用程序以使用新字体。")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+22 -5
View File
@@ -1,4 +1,26 @@
#!/bin/bash #!/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 set -e
@@ -9,7 +31,6 @@ NC='\033[0m'
SRC="$HOME/.dotfile/ghostty/config.ghostty" SRC="$HOME/.dotfile/ghostty/config.ghostty"
# 根据平台确定目标路径
if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname)" == "Darwin" ]]; then
DEST="$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty" DEST="$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty"
else else
@@ -20,22 +41,18 @@ fi
echo -e "${YELLOW}初始化 Ghostty 配置...${NC}" echo -e "${YELLOW}初始化 Ghostty 配置...${NC}"
echo -e "${YELLOW}目标路径:$DEST${NC}" echo -e "${YELLOW}目标路径:$DEST${NC}"
# 目标目录不存在则创建
mkdir -p "$(dirname "$DEST")" mkdir -p "$(dirname "$DEST")"
# 已经是指向正确位置的软链接,无需操作
if [ -L "$DEST" ] && [ "$(readlink -f "$DEST")" = "$(readlink -f "$SRC")" ]; then if [ -L "$DEST" ] && [ "$(readlink -f "$DEST")" = "$(readlink -f "$SRC")" ]; then
echo -e "${GREEN}软链接已存在且指向正确位置,无需操作。${NC}" echo -e "${GREEN}软链接已存在且指向正确位置,无需操作。${NC}"
exit 0 exit 0
fi fi
# 存在旧文件/目录则备份
if [ -e "$DEST" ]; then if [ -e "$DEST" ]; then
BACKUP="${DEST}.backup.$(date +%Y%m%d%H%M%S)" BACKUP="${DEST}.backup.$(date +%Y%m%d%H%M%S)"
mv "$DEST" "$BACKUP" mv "$DEST" "$BACKUP"
echo -e "${YELLOW}已将现有配置备份到 $BACKUP${NC}" echo -e "${YELLOW}已将现有配置备份到 $BACKUP${NC}"
elif [ -L "$DEST" ]; then elif [ -L "$DEST" ]; then
# 损坏的旧软链接
rm "$DEST" rm "$DEST"
fi fi
+24 -4
View File
@@ -1,11 +1,31 @@
#!/bin/bash #!/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' echo 'Init git config'
src=$HOME/.gitconfig src=$HOME/.gitconfig
if [ -f $src ]; then if [ -f "$src" ]; then
mv $src $src.bak mv "$src" "$src.bak"
fi fi
ln -s $HOME/.dotfile/git/.gitconfig $src ln -s "$HOME/.dotfile/git/.gitconfig" "$src"
chmod +x "$HOME/.dotfile/git/git-credential-linux.sh" chmod +x "$HOME/.dotfile/git/git-credential-linux.sh"
+21 -22
View File
@@ -1,32 +1,31 @@
#!/bin/bash #!/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 set -e
# install gvm
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) 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 JGAntunes/fish-gvm"
fish -c "fisher install edc/bass" fish -c "fisher install edc/bass"
# install go
fish -c "gvm install go1.24.0 -B" fish -c "gvm install go1.24.0 -B"
fish -c "gvm use go1.24.0 --default" 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
+20 -3
View File
@@ -1,9 +1,26 @@
#!/bin/bash #!/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 身份运行 # 检查是否以 root 身份运行
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
+25 -3
View File
@@ -1,4 +1,27 @@
#!/bin/bash #!/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 set -e
@@ -30,7 +53,6 @@ install_via_apt() {
sudo apt-get install -y lazygit sudo apt-get install -y lazygit
} }
# 从 GitHub Release 解压到 ~/.local/bin(与 nvim/init.lua 中的 PATH 一致)
install_via_release() { install_via_release() {
local ver local ver
ver=$(curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | ver=$(curl -fsSL "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" |
@@ -72,7 +94,7 @@ install_via_release() {
echo -e "${GREEN}已安装到 ${dest_bin}/lazygit${NC}" echo -e "${GREEN}已安装到 ${dest_bin}/lazygit${NC}"
if [[ ":${PATH}:" != *":${dest_bin}:"* ]]; then if [[ ":${PATH}:" != *":${dest_bin}:"* ]]; then
echo -e "${YELLOW}提示: 请将 ${dest_bin} 加入 PATH(例如在 ~/.zshrc 中 export PATH=\"\$HOME/.local/bin:\$PATH\")${NC}" echo -e "${YELLOW}提示: 请将 ${dest_bin} 加入 PATH${NC}"
fi fi
} }
@@ -84,7 +106,7 @@ main() {
elif install_via_release; then elif install_via_release; then
: :
else else
echo -e "${RED}lazygit 安装失败:未找到 brew/apt,且当前平台无法使用官方二进制发布包。${NC}" >&2 echo -e "${RED}lazygit 安装失败${NC}" >&2
exit 1 exit 1
fi fi
+27 -13
View File
@@ -1,8 +1,27 @@
#!/bin/bash #!/bin/bash
source $HOME/.dotfile/script/common/os-arch.sh # 下载并安装 mihomo 代理内核到 /usr/local/bin
# 检查并安装 gunzip SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
function check_gunzip() { # 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 if ! command -v gunzip &>/dev/null; then
echo "gunzip 未安装,正在安装..." echo "gunzip 未安装,正在安装..."
if command -v apt-get &>/dev/null; then if command -v apt-get &>/dev/null; then
@@ -18,8 +37,9 @@ function check_gunzip() {
fi fi
} }
function install_mihomo() { install_mihomo() {
local os_arch=$(get_os_arch) local os_arch
os_arch=$(get_os_arch)
local base_url="https://oss-default.yumee.top/bin/mihomo" local base_url="https://oss-default.yumee.top/bin/mihomo"
local url="" local url=""
@@ -34,14 +54,12 @@ function install_mihomo() {
;; ;;
esac esac
# 检查 gunzip 命令
check_gunzip check_gunzip
# 创建临时目录 local temp_dir
local temp_dir=$(mktemp -d) temp_dir=$(mktemp -d)
local temp_file="${temp_dir}/mihomo.gz" local temp_file="${temp_dir}/mihomo.gz"
# 下载文件
echo "正在从 ${url} 下载..." echo "正在从 ${url} 下载..."
if ! curl -L -o "${temp_file}" "${url}"; then if ! curl -L -o "${temp_file}" "${url}"; then
echo "错误: 下载失败" echo "错误: 下载失败"
@@ -49,7 +67,6 @@ function install_mihomo() {
exit 1 exit 1
fi fi
# 解压文件
echo "正在解压..." echo "正在解压..."
if ! gunzip "${temp_file}"; then if ! gunzip "${temp_file}"; then
echo "错误: 解压失败" echo "错误: 解压失败"
@@ -57,17 +74,14 @@ function install_mihomo() {
exit 1 exit 1
fi fi
# 移动到 /usr/local/bin 并设置执行权限
local binary="${temp_dir}/mihomo" local binary="${temp_dir}/mihomo"
echo "正在安装到 /usr/local/bin/mihomo..." echo "正在安装到 /usr/local/bin/mihomo..."
sudo mv "${binary}" /usr/local/bin/mihomo sudo mv "${binary}" /usr/local/bin/mihomo
sudo chmod +x /usr/local/bin/mihomo sudo chmod +x /usr/local/bin/mihomo
# 清理临时文件
rm -rf "${temp_dir}" rm -rf "${temp_dir}"
echo "mihomo 安装完成!" echo "mihomo 安装完成!"
echo "版本信息:"
/usr/local/bin/mihomo version || echo "mihomo 已安装到 /usr/local/bin/mihomo" /usr/local/bin/mihomo version || echo "mihomo 已安装到 /usr/local/bin/mihomo"
} }
+28 -21
View File
@@ -1,30 +1,45 @@
#!/bin/bash #!/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 set -e
# 设置颜色
GREEN='\033[0;32m' GREEN='\033[0;32m'
RED='\033[0;31m' RED='\033[0;31m'
YELLOW='\033[1;33m' 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}" echo -e "${YELLOW}mise 未安装. 正在安装 mise...${NC}"
# 安装 mise(官方安装脚本会自动配置 shell)
curl https://mise.run | sh curl https://mise.run | sh
# 重新加载当前 shell 环境
export PATH="$HOME/.local/bin:$PATH" export PATH="$HOME/.local/bin:$PATH"
# 如果安装脚本没有自动配置,手动配置 fish shell(如果存在)
if [ -f ~/.config/fish/config.fish ]; then if [ -f ~/.config/fish/config.fish ]; then
if ! grep -q "mise activate" ~/.config/fish/config.fish; then if ! grep -q "mise activate" ~/.config/fish/config.fish; then
echo -e "${YELLOW}正在为 fish shell 配置 mise...${NC}" echo -e "${YELLOW}正在为 fish shell 配置 mise...${NC}"
echo '' >> ~/.config/fish/config.fish {
echo '# mise (formerly rtx)' >> ~/.config/fish/config.fish echo ''
echo 'mise activate fish | source' >> ~/.config/fish/config.fish echo '# mise (formerly rtx)'
echo 'mise activate fish | source'
} >>~/.config/fish/config.fish
echo -e "${GREEN}已将 mise 添加到 ~/.config/fish/config.fish${NC}" echo -e "${GREEN}已将 mise 添加到 ~/.config/fish/config.fish${NC}"
fi fi
fi fi
@@ -35,7 +50,6 @@ else
mise --version mise --version
fi fi
# 链接 dotfile 中的 mise 配置
MISE_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mise" MISE_CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mise"
DOTFILE_MISE_CONFIG="$HOME/.dotfile/mise/config.toml" DOTFILE_MISE_CONFIG="$HOME/.dotfile/mise/config.toml"
mkdir -p "$MISE_CONFIG_DIR" mkdir -p "$MISE_CONFIG_DIR"
@@ -49,22 +63,15 @@ if [ -f "$DOTFILE_MISE_CONFIG" ]; then
mise trust "$DOTFILE_MISE_CONFIG" 2>/dev/null || true mise trust "$DOTFILE_MISE_CONFIG" 2>/dev/null || true
fi 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}" echo -e "${YELLOW}正在安装 mise 工具...${NC}"
mise install mise install
echo -e "${GREEN}mise 工具安装完成${NC}" echo -e "${GREEN}mise 工具安装完成${NC}"
fi fi
# 验证安装 if command -v mise &>/dev/null; then
if command -v mise &> /dev/null
then
echo -e "${GREEN}mise 安装成功!${NC}" 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 else
echo -e "${RED}mise 安装失败,请检查错误信息${NC}" echo -e "${RED}mise 安装失败,请检查错误信息${NC}"
exit 1 exit 1
fi fi
+9 -5
View File
@@ -1,7 +1,9 @@
#!/bin/bash #!/bin/bash
# 为指定用户配置某命令的免密 sudo(写入 /etc/sudoers.d/) # 为指定用户配置某命令的免密 sudo(写入 /etc/sudoers.d/)
# 用法: sudo nosudo <user> <command>
# 示例: sudo nosudo xuser docker SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/common/help.sh"
set -euo pipefail set -euo pipefail
@@ -21,12 +23,14 @@ usage() {
- 仅允许免密执行解析到的那个可执行文件路径(含子命令参数仍受 sudo 规则约束) - 仅允许免密执行解析到的那个可执行文件路径(含子命令参数仍受 sudo 规则约束)
- 对 docker,更推荐: sudo docker-group <user>(无需 sudo 即可用 docker) - 对 docker,更推荐: sudo docker-group <user>(无需 sudo 即可用 docker)
- 删除规则: sudo rm /etc/sudoers.d/nosudo-<user>-<name> - 删除规则: sudo rm /etc/sudoers.d/nosudo-<user>-<name>
选项:
-h, --help 显示此帮助
EOF EOF
exit 1
} }
[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage dotfile_help_requested "${1:-}" && dotfile_show_help
[[ $# -eq 2 ]] || usage [[ $# -eq 2 ]] || { usage; exit 1; }
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
echo "错误: 请使用 sudo 运行,例如: sudo $0 $*" echo "错误: 请使用 sudo 运行,例如: sudo $0 $*"
+21 -10
View File
@@ -1,38 +1,49 @@
#!/bin/bash #!/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" 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 if [ -L "$NVIM_CONFIG_DIR" ] && [ "$(readlink -f "$NVIM_CONFIG_DIR")" = "$(readlink -f "$YOUR_CUSTOM_CONFIG_DIR")" ]; then
echo "软链接已经存在并指向正确的目录。无需进行任何操作。" echo "软链接已经存在并指向正确的目录。无需进行任何操作。"
exit 0 exit 0
fi fi
# 检查是否存在现有的 Neovim 配置
if [ -e "$NVIM_CONFIG_DIR" ]; then if [ -e "$NVIM_CONFIG_DIR" ]; then
echo "检测到现有的 Neovim 配置。" echo "检测到现有的 Neovim 配置。"
# 如果是目录或常规文件,创建备份
if [ -d "$NVIM_CONFIG_DIR" ] || [ -f "$NVIM_CONFIG_DIR" ]; then if [ -d "$NVIM_CONFIG_DIR" ] || [ -f "$NVIM_CONFIG_DIR" ]; then
BACKUP_DIR="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d%H%M%S)" BACKUP_DIR="$NVIM_CONFIG_DIR.backup.$(date +%Y%m%d%H%M%S)"
mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR" mv "$NVIM_CONFIG_DIR" "$BACKUP_DIR"
echo "已将现有配置备份到 $BACKUP_DIR" echo "已将现有配置备份到 $BACKUP_DIR"
else else
# 如果是其他类型的文件(如损坏的软链接),直接删除
rm "$NVIM_CONFIG_DIR" rm "$NVIM_CONFIG_DIR"
echo "已删除现有的无效配置。" echo "已删除现有的无效配置。"
fi fi
fi fi
# 创建软链接
ln -s "$YOUR_CUSTOM_CONFIG_DIR" "$NVIM_CONFIG_DIR" 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 if [ -L "$NVIM_CONFIG_DIR" ] && [ "$(readlink -f "$NVIM_CONFIG_DIR")" = "$(readlink -f "$YOUR_CUSTOM_CONFIG_DIR")" ]; then
echo "软链接创建成功。Neovim 现在将使用您的自定义配置。" echo "软链接创建成功。Neovim 现在将使用您的自定义配置。"
else else
echo "创建软链接时出错。请检查路径和权限。" echo "创建软链接时出错。请检查路径和权限。"
exit 1
fi fi
+30 -18
View File
@@ -1,8 +1,28 @@
#!/bin/bash #!/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 set -e
# install neovim
os_arch=$(uname -s)-$(uname -m) os_arch=$(uname -s)-$(uname -m)
ver=v0.12.2 ver=v0.12.2
@@ -10,57 +30,49 @@ case $os_arch in
"Darwin-x86_64") "Darwin-x86_64")
cd /tmp cd /tmp
rm -rf nvim-macos-x86_64.tar.gz 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 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 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/vim
sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim
echo ""
vim -v vim -v
;; ;;
"Darwin-arm64" | "Darwin-aarch64") "Darwin-arm64" | "Darwin-aarch64")
cd /tmp cd /tmp
rm -rf nvim-macos-arm64.tar.gz 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 tar zxf nvim-macos-arm64.tar.gz
[ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak [ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak
sudo mv nvim-macos-arm64 /usr/local/nvim 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/vim
sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim
echo ""
vim -v vim -v
;; ;;
"Linux-x86_64") "Linux-x86_64")
cd /tmp cd /tmp
rm -rf nvim-linux64.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 wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-x86_64.tar.gz"
tar zxf 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 [ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak
sudo mv nvim-linux-x86_64 /usr/local/nvim 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/vim
sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim
echo ""
vim -v vim -v
;; ;;
"Linux-arm64" | "Linux-aarch64") "Linux-arm64" | "Linux-aarch64")
cd /tmp cd /tmp
rm -rf 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 wget "https://github.com/neovim/neovim/releases/download/$ver/nvim-linux-arm64.tar.gz"
tar zxf nvim-linux64.tar.gz tar zxf nvim-linux-arm64.tar.gz
[ -e /usr/local/nvim ] && sudo mv -f /usr/local/nvim /usr/local/nvim-bak [ -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/vim
sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim sudo ln -sf /usr/local/nvim/bin/nvim /usr/local/bin/nvim
echo ""
vim -v vim -v
;; ;;
*) *)
echo "不支持的平台: $os_arch" >&2 echo "不支持的平台: $os_arch" >&2
exit 1 exit 1
;; ;;
esac esac
# if has error "no c compile found"
# sudo apt install build-essential
+26 -4
View File
@@ -1,9 +1,31 @@
#!/bin/bash #!/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 curl -fsSL https://fnm.vercel.app/install | bash
if command -v ff &>/dev/null; then
ff reload ff reload
elif command -v fish &>/dev/null; then
# after install node fish -c "source ~/.config/fish/config.fish"
# corepack enable pnpm fi
+28 -15
View File
@@ -1,38 +1,51 @@
#!/bin/bash #!/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 set -e
# 设置颜色
GREEN='\033[0;32m' GREEN='\033[0;32m'
RED='\033[0;31m' 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}" echo -e "${RED}pyenv 未安装. 正在安装 pyenv...${NC}"
# 安装 pyenv
curl https://pyenv.run | bash 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 else
echo -e "${GREEN}pyenv 已安装${NC}" echo -e "${GREEN}pyenv 已安装${NC}"
fi fi
# 询问用户想安装的 Python 版本
python_version=3.11.10 python_version=3.11.10
# 安装指定版本的 Python
echo -e "${GREEN}正在安装 Python $python_version...${NC}" 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) installed_version=$(python --version)
echo -e "${GREEN}Python 安装完成. 当前版本: $installed_version${NC}" echo -e "${GREEN}Python 安装完成. 当前版本: $installed_version${NC}"
# 安装一些常用的 Python 包
echo -e "${GREEN}正在安装一些常用的 Python 包...${NC}" echo -e "${GREEN}正在安装一些常用的 Python 包...${NC}"
pip install --upgrade pip pip install --upgrade pip
pip install ipython jupyter notebook pandas numpy matplotlib scikit-learn pip install ipython jupyter notebook pandas numpy matplotlib scikit-learn
+26 -19
View File
@@ -1,12 +1,29 @@
#!/bin/bash #!/bin/bash
# ICMP/TCP 扫描本网段或指定网段的在线主机与开放端口
# 用法: SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ./scan-ip.sh # ICMP 扫描本网段在线 IP # shellcheck disable=SC1091
# ./scan-ip.sh 10.0.0 # ICMP 扫描指定网段(10.0.0.1-254) source "$SCRIPT_DIR/common/help.sh"
# ./scan-ip.sh 22 # 扫描本网段中 22 端口开放的 IP
# ./scan-ip.sh 80,443,8080 # 扫描本网段中任一指定端口开放的 IP usage() {
# ./scan-ip.sh 22 10.0.0 # 指定网段前缀 + 端口(顺序不限) cat <<'EOF'
# ./scan-ip.sh 10.0.0 22 # 同上 用法: 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 TIMEOUT=1
@@ -31,8 +48,7 @@ fi
if [ -n "$1" ] && [ -z "$PORT_SPEC" ] && [ -z "$PREFIX_ARG" ]; then if [ -n "$1" ] && [ -z "$PORT_SPEC" ] && [ -z "$PREFIX_ARG" ]; then
echo "错误: 无法识别参数: $1" echo "错误: 无法识别参数: $1"
echo "端口支持: 单端口(22) / 列表(80,443) / 范围(8000-8100)" usage
echo "网段支持: 前缀(10.0.0 或 192.168.1.0)"
exit 1 exit 1
fi fi
@@ -52,7 +68,6 @@ else
PREFIX="${LOCAL_IP%.*}" PREFIX="${LOCAL_IP%.*}"
fi fi
# 解析端口参数 -> PORTS 数组
PORTS=() PORTS=()
if [ -n "$PORT_SPEC" ]; then if [ -n "$PORT_SPEC" ]; then
if [[ "$PORT_SPEC" =~ ^[0-9]+$ ]]; then if [[ "$PORT_SPEC" =~ ^[0-9]+$ ]]; then
@@ -68,7 +83,6 @@ if [ -n "$PORT_SPEC" ]; then
IFS=',' read -ra PORTS <<<"$PORT_SPEC" IFS=',' read -ra PORTS <<<"$PORT_SPEC"
else else
echo "错误: 无效的端口格式: $PORT_SPEC" echo "错误: 无效的端口格式: $PORT_SPEC"
echo "支持: 单端口(22) / 列表(80,443) / 范围(8000-8100)"
exit 1 exit 1
fi fi
fi fi
@@ -81,10 +95,7 @@ else
fi fi
echo "------------------------------------------------------" echo "------------------------------------------------------"
# ---------- 模式一:ICMP 在线扫描(无端口参数) ----------
if [ ${#PORTS[@]} -eq 0 ]; then if [ ${#PORTS[@]} -eq 0 ]; then
# macOS: -W 单位毫秒,-S 绑定源 IP
# Linux: -W 单位秒, -I 绑定源 IP
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
for i in $(seq 1 254); do for i in $(seq 1 254); do
ping -c 1 -W 1000 -S "$LOCAL_IP" "$PREFIX.$i" >/dev/null 2>&1 && echo "$PREFIX.$i 在线" & 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 exit 0
fi fi
# ---------- 模式二:TCP 端口扫描 ----------
if ! command -v nc &>/dev/null; then if ! command -v nc &>/dev/null; then
echo "错误: 找不到 nc (netcat),请先安装:" echo "错误: 找不到 nc (netcat)"
echo " macOS: brew install netcat"
echo " Debian/Ubuntu: sudo apt-get install netcat-openbsd"
exit 1 exit 1
fi fi
# nc 在不同实现里 -z/-w 行为基本一致;用 -G 在 macOS 上控制连接超时更准
NC_OPTS=(-z -w "$TIMEOUT") NC_OPTS=(-z -w "$TIMEOUT")
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
NC_OPTS+=(-G "$TIMEOUT") NC_OPTS+=(-G "$TIMEOUT")
+31 -35
View File
@@ -1,25 +1,42 @@
#!/bin/bash #!/bin/bash
# 对指定 IP 进行 TCP 端口扫描
# 端口扫描脚本 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 用法: ./scan-port.sh <IP地址> [端口范围] # shellcheck disable=SC1091
# 示例: ./scan-port.sh 192.168.1.1 source "$SCRIPT_DIR/common/help.sh"
# 示例: ./scan-port.sh 192.168.1.1 80,443,8080
# 示例: ./scan-port.sh 192.168.1.1 1-1000 usage() {
cat <<'EOF'
用法: scan-port <IP> [端口范围]
扫描目标 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 if [ -z "$1" ]; then
echo "错误: 请提供要扫描的 IP 地址" echo "错误: 请提供要扫描的 IP 地址" >&2
echo "用法: $0 <IP地址> [端口范围]" usage
echo "示例: $0 192.168.1.1"
echo "示例: $0 192.168.1.1 80,443,8080"
echo "示例: $0 192.168.1.1 1-1000"
exit 1 exit 1
fi fi
TARGET_IP="$1" 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 if ! [[ "$TARGET_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "错误: 无效的 IP 地址格式: $TARGET_IP" echo "错误: 无效的 IP 地址格式: $TARGET_IP"
exit 1 exit 1
@@ -29,25 +46,17 @@ echo "=========================================="
echo "正在扫描 IP: $TARGET_IP" echo "正在扫描 IP: $TARGET_IP"
echo "==========================================" echo "=========================================="
# 检查是否有 nc (netcat) 命令
if ! command -v nc &>/dev/null; then if ! command -v nc &>/dev/null; then
echo "错误: 找不到 nc (netcat) 命令。" echo "错误: 找不到 nc (netcat) 命令。"
echo "请安装 netcat:"
echo " macOS: brew install netcat"
echo " Linux: sudo apt-get install netcat 或 sudo yum install nc"
exit 1 exit 1
fi fi
# 确定要扫描的端口
if [ -z "$2" ]; then 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) PORTS=(21 22 23 25 53 80 110 111 135 139 143 443 445 993 995 1723 3306 3389 5432 5900 8080 8443)
echo "使用默认常用端口列表" echo "使用默认常用端口列表"
else else
# 解析端口参数
PORT_SPEC="$2" PORT_SPEC="$2"
# 检查是否是范围格式 (例如: 1-1000)
if [[ "$PORT_SPEC" =~ ^[0-9]+-[0-9]+$ ]]; then if [[ "$PORT_SPEC" =~ ^[0-9]+-[0-9]+$ ]]; then
START_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f1) START_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f1)
END_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f2) END_PORT=$(echo "$PORT_SPEC" | cut -d'-' -f2)
@@ -58,16 +67,12 @@ else
fi fi
PORTS=($(seq "$START_PORT" "$END_PORT")) PORTS=($(seq "$START_PORT" "$END_PORT"))
echo "扫描端口范围: $START_PORT-$END_PORT (共 $((END_PORT - START_PORT + 1)) 个端口)" echo "扫描端口范围: $START_PORT-$END_PORT"
# 检查是否是逗号分隔的端口列表 (例如: 80,443,8080)
elif [[ "$PORT_SPEC" =~ ^[0-9,]+$ ]]; then elif [[ "$PORT_SPEC" =~ ^[0-9,]+$ ]]; then
IFS=',' read -ra PORTS <<<"$PORT_SPEC" IFS=',' read -ra PORTS <<<"$PORT_SPEC"
echo "扫描指定端口: $PORT_SPEC" echo "扫描指定端口: $PORT_SPEC"
else else
echo "错误: 无效的端口格式: $PORT_SPEC" echo "错误: 无效的端口格式: $PORT_SPEC"
echo "支持的格式:"
echo " - 逗号分隔: 80,443,8080"
echo " - 范围: 1-1000"
exit 1 exit 1
fi fi
fi fi
@@ -75,20 +80,14 @@ fi
echo "超时设置: ${TIMEOUT}秒" echo "超时设置: ${TIMEOUT}秒"
echo "------------------------------------------------------" echo "------------------------------------------------------"
# 统计变量
OPEN_PORTS=() OPEN_PORTS=()
CLOSED_COUNT=0 CLOSED_COUNT=0
TOTAL_PORTS=${#PORTS[@]} TOTAL_PORTS=${#PORTS[@]}
# 扫描端口函数
scan_port() { scan_port() {
local port=$1 local port=$1
local ip=$2 local ip=$2
# 使用 nc 扫描端口
# -z: 只扫描,不发送数据
# -v: 详细输出
# -w: 超时时间
if nc -z -v -w "$TIMEOUT" "$ip" "$port" 2>&1 | grep -q "succeeded\|open"; then if nc -z -v -w "$TIMEOUT" "$ip" "$port" 2>&1 | grep -q "succeeded\|open"; then
echo "✅ 端口 $port: 开放" echo "✅ 端口 $port: 开放"
return 0 return 0
@@ -97,7 +96,6 @@ scan_port() {
fi fi
} }
# 并行扫描端口(使用后台任务)
for port in "${PORTS[@]}"; do for port in "${PORTS[@]}"; do
if scan_port "$port" "$TARGET_IP"; then if scan_port "$port" "$TARGET_IP"; then
OPEN_PORTS+=("$port") OPEN_PORTS+=("$port")
@@ -106,7 +104,6 @@ for port in "${PORTS[@]}"; do
fi fi
done done
# 等待所有后台任务完成
wait wait
echo "------------------------------------------------------" echo "------------------------------------------------------"
@@ -128,4 +125,3 @@ else
fi fi
echo "==========================================" echo "=========================================="
+27 -6
View File
@@ -1,12 +1,33 @@
#/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' echo 'Init tmux config'
src=$HOME/.tmux.conf src=$HOME/.tmux.conf
if [ -e $src ]; then if [ -e "$src" ]; then
mv $src $src.bak mv "$src" "$src.bak"
fi fi
ln -s $HOME/.dotfile/tmux/.tmux.conf $src ln -s "$HOME/.dotfile/tmux/.tmux.conf" "$src"
echo 'Install tpm' echo 'Install tpm'
if [ ! -e ~/.tmux/plugins/tpm ]; then if [ ! -e ~/.tmux/plugins/tpm ]; then
@@ -14,7 +35,7 @@ if [ ! -e ~/.tmux/plugins/tpm ]; then
fi fi
echo 'Install tmux plugin by tpm' echo 'Install tmux plugin by tpm'
$HOME/.tmux/plugins/tpm/bin/install_plugins "$HOME/.tmux/plugins/tpm/bin/install_plugins"
echo 'Reload tmux' echo 'Reload tmux'
tmux source ~/.tmux.conf tmux source ~/.tmux.conf
+24 -3
View File
@@ -1,6 +1,28 @@
#!/bin/bash #!/bin/bash
# 切换 CN_REGION 环境变量 # 切换 CN_REGION 环境变量(国内/国外镜像与安装源)
# cn ↔ intl 切换
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 if [ "$CN_REGION" = "1" ]; then
export CN_REGION=0 export CN_REGION=0
@@ -9,4 +31,3 @@ else
export CN_REGION=1 export CN_REGION=1
echo "[INFO] Region switched to: 1 (国内)" echo "[INFO] Region switched to: 1 (国内)"
fi fi
+26 -3
View File
@@ -1,4 +1,27 @@
#!/bin/bash #!/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 set -euo pipefail
DOTFILE="${DOTFILE:-$HOME/.dotfile}" DOTFILE="${DOTFILE:-$HOME/.dotfile}"
@@ -14,7 +37,7 @@ ln -sf "$ZSHRC_REPO" "$ZSHRC_HOME"
echo " $ZSHRC_HOME -> $ZSHRC_REPO" echo " $ZSHRC_HOME -> $ZSHRC_REPO"
if [[ ! -d "$HOME/.oh-my-zsh" ]]; then if [[ ! -d "$HOME/.oh-my-zsh" ]]; then
echo "安装 Oh My Zsh(--unattended:安装脚本内不改 shell、不自动进 zsh;--keep-zshrc:保留已链接的 .zshrc)…" echo "安装 Oh My Zsh…"
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended --keep-zshrc sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended --keep-zshrc
else else
echo "Oh My Zsh 已存在,跳过安装 (~/.oh-my-zsh)" echo "Oh My Zsh 已存在,跳过安装 (~/.oh-my-zsh)"
@@ -35,5 +58,5 @@ fi
chsh -s "$zshpath" chsh -s "$zshpath"
echo "" echo ""
echo "完成。当前仓库的 .zshrc 已启用 z 插件(Oh My Zsh plugins/z),默认 shell 已改为: $zshpath" echo "完成。默认 shell 已改为: $zshpath"
echo "请重新登录或新开终端后生效。本机额外配置可写入 ~/.zshrc.local(不会被链接覆盖)。" echo "请重新登录或新开终端后生效。"