first commit
This commit is contained in:
64
.gitignore
vendored
Normal file
64
.gitignore
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Node/React
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Go
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# 数据文件
|
||||||
|
data/data.json
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# 操作系统
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# 其他
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
8614
change-mirror/ChangeMirrors.sh
Normal file
8614
change-mirror/ChangeMirrors.sh
Normal file
File diff suppressed because it is too large
Load Diff
2569
change-mirror/DockerInstallation.sh
Normal file
2569
change-mirror/DockerInstallation.sh
Normal file
File diff suppressed because it is too large
Load Diff
1770
change-mirror/DockerInstallationLite.sh
Normal file
1770
change-mirror/DockerInstallationLite.sh
Normal file
File diff suppressed because it is too large
Load Diff
179
docker-info/AGENTS.md
Normal file
179
docker-info/AGENTS.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
# AGENTS.md - Agentic Coding Guidelines
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a Bash shell script project for collecting Docker and server information on Debian/Ubuntu systems.
|
||||||
|
|
||||||
|
**Primary Language**: Bash
|
||||||
|
**Main File**: `docker-info.sh`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build/Lint/Test Commands
|
||||||
|
|
||||||
|
Since this is a Bash script project without a formal build system, use these commands:
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
```bash
|
||||||
|
# ShellCheck (static analysis for bash scripts)
|
||||||
|
shellcheck docker-info.sh
|
||||||
|
|
||||||
|
# Check for syntax errors
|
||||||
|
bash -n docker-info.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
# Run the script (requires Docker to be installed for full functionality)
|
||||||
|
bash docker-info.sh
|
||||||
|
|
||||||
|
# Or make executable and run directly
|
||||||
|
chmod +x docker-info.sh
|
||||||
|
./docker-info.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Single Test Pattern
|
||||||
|
```bash
|
||||||
|
# Test specific functions by sourcing and calling them
|
||||||
|
source docker-info.sh && command_exists docker
|
||||||
|
source docker-info.sh && print_success "test message"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### General Bash Style
|
||||||
|
|
||||||
|
- **Shebang**: Always use `#!/usr/bin/env bash`
|
||||||
|
- **Set options**: Use `set -euo pipefail` at the start of scripts
|
||||||
|
- `-e`: Exit on error
|
||||||
|
- `-u`: Exit on undefined variables
|
||||||
|
- `-o pipefail`: Pipeline fails if any command fails
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
- **Indentation**: 4 spaces (no tabs)
|
||||||
|
- **Line length**: Keep lines under 100 characters
|
||||||
|
- **Quotes**: Always quote variables: `"$variable"` not `$variable`
|
||||||
|
- **Functions**: Use `snake_case` for function names
|
||||||
|
- **Constants**: Use `UPPER_CASE` for constants and color codes
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Functions - snake_case
|
||||||
|
print_header() { }
|
||||||
|
get_server_info() { }
|
||||||
|
check_root() { }
|
||||||
|
|
||||||
|
# Variables - descriptive names
|
||||||
|
local container_output
|
||||||
|
local cpu_count
|
||||||
|
local total_mem
|
||||||
|
|
||||||
|
# Constants - UPPER_CASE
|
||||||
|
readonly RED='\033[0;31m'
|
||||||
|
readonly HEADER_COLOR="${BRIGHT_CYAN}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Always check command existence before use
|
||||||
|
command_exists() {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use proper exit codes
|
||||||
|
if ! command_exists docker; then
|
||||||
|
print_error "Docker未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Redirect errors appropriately
|
||||||
|
docker ps 2>/dev/null || print_warning "无法列出容器"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Function Structure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Document functions with comments
|
||||||
|
# 获取服务器信息
|
||||||
|
get_server_info() {
|
||||||
|
print_header "服务器信息"
|
||||||
|
|
||||||
|
# Use local for function-scoped variables
|
||||||
|
local cpu_count=$(grep -c '^processor' /proc/cpuinfo)
|
||||||
|
local cpu_model=$(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)
|
||||||
|
|
||||||
|
# Call other functions for output
|
||||||
|
print_key_value "CPU核心数" "$cpu_count"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Color/Styling Code
|
||||||
|
|
||||||
|
- Define color codes as constants at the top of the file
|
||||||
|
- Group related colors together (basic, bright, background, styles)
|
||||||
|
- Use descriptive names: `HEADER_COLOR`, `SUCCESS_COLOR`, `ERROR_COLOR`
|
||||||
|
- Always reset colors with `${NC}` after colored output
|
||||||
|
|
||||||
|
### Output Patterns
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use helper functions for consistent output
|
||||||
|
print_header() # Boxed titles
|
||||||
|
print_section() # Section headers with arrows
|
||||||
|
print_key_value() # Key: Value pairs
|
||||||
|
print_success() # Green checkmark
|
||||||
|
print_warning() # Yellow warning
|
||||||
|
print_error() # Red X
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
|
||||||
|
- Use Chinese comments to match existing codebase style
|
||||||
|
- Keep comments concise and meaningful
|
||||||
|
- Comment complex logic or non-obvious behavior
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
Before submitting changes:
|
||||||
|
|
||||||
|
- [ ] Run `shellcheck docker-info.sh` with no warnings
|
||||||
|
- [ ] Run `bash -n docker-info.sh` for syntax check
|
||||||
|
- [ ] Test on a system with Docker installed
|
||||||
|
- [ ] Test on a system without Docker (should handle gracefully)
|
||||||
|
- [ ] Verify all color output displays correctly
|
||||||
|
- [ ] Check that tables align properly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
**Required**: None (uses standard POSIX utilities)
|
||||||
|
|
||||||
|
**Optional but recommended**:
|
||||||
|
- `jq` - For better JSON parsing of Docker info
|
||||||
|
- `shellcheck` - For linting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/shumengya/project/bash/docker-info/
|
||||||
|
└── docker-info.sh # Main script file
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- This script may require root access for some Docker operations
|
||||||
|
- Always validate user input if added in the future
|
||||||
|
- Use proper quoting to prevent word splitting and globbing
|
||||||
|
- Sanitize any data passed to eval or similar dangerous operations
|
||||||
253
docker-info/docker-info.sh
Executable file
253
docker-info/docker-info.sh
Executable file
@@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Info Collector - Single Column Edition
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Colors
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
readonly NC='\033[0m'
|
||||||
|
readonly GRAY='\033[0;90m'
|
||||||
|
readonly CYAN='\033[0;36m'
|
||||||
|
readonly GREEN='\033[1;32m'
|
||||||
|
readonly YELLOW='\033[1;33m'
|
||||||
|
readonly WHITE='\033[1;37m'
|
||||||
|
readonly DIM='\033[2m'
|
||||||
|
|
||||||
|
readonly C_HEADER="${CYAN}"
|
||||||
|
readonly C_SECTION="${YELLOW}"
|
||||||
|
readonly C_KEY="${WHITE}"
|
||||||
|
readonly C_VALUE="${CYAN}"
|
||||||
|
readonly C_OK="${GREEN}"
|
||||||
|
readonly C_WARN="${YELLOW}"
|
||||||
|
readonly C_ERR="\033[1;31m"
|
||||||
|
readonly C_DIM="${GRAY}"
|
||||||
|
|
||||||
|
# Separator line (thick line style)
|
||||||
|
SEP_LINE="${C_DIM}══════════════════════════════════════════════════════════════════════════════${NC}"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Utils
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
has_cmd() { command -v "$1" &>/dev/null; }
|
||||||
|
|
||||||
|
print_header() {
|
||||||
|
local title="$1"
|
||||||
|
local len=${#title}
|
||||||
|
local pad=$(( (76 - len) / 2 ))
|
||||||
|
echo -e "\n${C_HEADER}╔══════════════════════════════════════════════════════════════════════════════╗"
|
||||||
|
printf "${C_HEADER}║%${pad}s${WHITE} %s %${pad}s║${NC}\n" "" "$title" ""
|
||||||
|
echo -e "${C_HEADER}╚══════════════════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_section() {
|
||||||
|
echo -e "\n${C_SECTION}▶ $1${NC}"
|
||||||
|
echo -e "$SEP_LINE"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_kv() {
|
||||||
|
printf " ${C_KEY}%-14s${NC} ${C_VALUE}%s${NC}\n" "$1:" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_ok() { echo -e " ${C_OK}●${NC} $1"; }
|
||||||
|
print_warn() { echo -e " ${C_WARN}●${NC} $1"; }
|
||||||
|
print_err() { echo -e " ${C_ERR}●${NC} $1"; }
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# System Info
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
collect_server() {
|
||||||
|
print_header "服务器信息"
|
||||||
|
|
||||||
|
# OS Information
|
||||||
|
print_section "操作系统"
|
||||||
|
local os_name kernel arch
|
||||||
|
os_name="$(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || uname -s)"
|
||||||
|
kernel="$(uname -r)"
|
||||||
|
arch="$(uname -m)"
|
||||||
|
|
||||||
|
print_kv "系统" "$os_name"
|
||||||
|
print_kv "内核版本" "$kernel"
|
||||||
|
print_kv "系统架构" "$arch"
|
||||||
|
|
||||||
|
# Host Info
|
||||||
|
print_section "主机信息"
|
||||||
|
local hostname uptime_str
|
||||||
|
hostname="$(hostname)"
|
||||||
|
uptime_str="$(uptime -p 2>/dev/null | sed 's/up //' || uptime | sed 's/.*up //; s/,.*//')"
|
||||||
|
|
||||||
|
print_kv "主机名" "$hostname"
|
||||||
|
print_kv "运行时长" "$uptime_str"
|
||||||
|
print_kv "当前时间" "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
|
||||||
|
# CPU
|
||||||
|
if [[ -f /proc/cpuinfo ]]; then
|
||||||
|
print_section "处理器"
|
||||||
|
local cpus cpu_model
|
||||||
|
cpus=$(grep -c '^processor' /proc/cpuinfo)
|
||||||
|
cpu_model=$(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)
|
||||||
|
print_kv "核心数量" "${cpus} 核"
|
||||||
|
print_kv "型号" "$cpu_model"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Memory
|
||||||
|
if has_cmd free; then
|
||||||
|
print_section "内存"
|
||||||
|
local mem_total mem_used mem_free mem_pct
|
||||||
|
mem_total=$(free -h | awk '/^Mem:/ {print $2}')
|
||||||
|
mem_used=$(free -h | awk '/^Mem:/ {print $3}')
|
||||||
|
mem_free=$(free -h | awk '/^Mem:/ {print $7}')
|
||||||
|
mem_pct=$(free | awk '/^Mem:/ {printf "%.1f", $3/$2 * 100}')
|
||||||
|
|
||||||
|
print_kv "总容量" "$mem_total"
|
||||||
|
print_kv "已使用" "$mem_used (${mem_pct}%)"
|
||||||
|
print_kv "可用" "$mem_free"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Disk
|
||||||
|
if has_cmd df; then
|
||||||
|
print_section "磁盘使用"
|
||||||
|
|
||||||
|
df -h --output=source,fstype,size,used,pcent,target 2>/dev/null | tail -n +2 | while read -r fs type size used pct target; do
|
||||||
|
[[ "$fs" == "tmpfs" || "$fs" == "devtmpfs" || "$fs" == "overlay" ]] && continue
|
||||||
|
[[ -z "$fs" ]] && continue
|
||||||
|
# Escape % for printf
|
||||||
|
local pct_clean="${pct%%%}"
|
||||||
|
printf " ${C_DIM}[${NC}${C_VALUE}%-12s${NC}${C_DIM}]${NC} ${C_KEY}%-8s${NC} ${C_VALUE}%-7s${NC} ${C_DIM}used:${NC} ${C_VALUE}%-7s${NC} ${C_DIM}(%s%%)${NC}\n" \
|
||||||
|
"$target" "$type" "$size" "$used" "$pct_clean"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Network
|
||||||
|
if has_cmd ip; then
|
||||||
|
print_section "网络接口"
|
||||||
|
|
||||||
|
ip -o addr show 2>/dev/null | awk '{print $2, $4}' | while read -r iface addr; do
|
||||||
|
[[ "$iface" == "lo" ]] && continue
|
||||||
|
print_kv "$iface" "$addr"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Info
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
collect_docker() {
|
||||||
|
print_header "Docker 信息"
|
||||||
|
|
||||||
|
if ! has_cmd docker; then
|
||||||
|
print_err "Docker 未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Version
|
||||||
|
print_section "版本信息"
|
||||||
|
local client_ver server_ver
|
||||||
|
client_ver=$(docker version --format '{{.Client.Version}}' 2>/dev/null || echo "N/A")
|
||||||
|
server_ver=$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo "N/A")
|
||||||
|
|
||||||
|
print_kv "客户端" "$client_ver"
|
||||||
|
print_kv "服务端" "$server_ver"
|
||||||
|
if has_cmd docker-compose; then
|
||||||
|
print_kv "Docker Compose" "$(docker-compose version --short 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Status
|
||||||
|
if docker info &>/dev/null; then
|
||||||
|
print_ok "守护进程运行中"
|
||||||
|
else
|
||||||
|
print_warn "守护进程未运行"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
print_section "资源统计"
|
||||||
|
local containers running images networks volumes
|
||||||
|
containers=$(docker ps -aq 2>/dev/null | wc -l)
|
||||||
|
running=$(docker ps -q 2>/dev/null | wc -l)
|
||||||
|
images=$(docker images -q 2>/dev/null | wc -l)
|
||||||
|
networks=$(docker network ls -q 2>/dev/null | wc -l)
|
||||||
|
volumes=$(docker volume ls -q 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
print_kv "容器" "${running} 运行 / ${containers} 总计"
|
||||||
|
print_kv "镜像" "$images"
|
||||||
|
print_kv "网络" "$networks"
|
||||||
|
print_kv "存储卷" "$volumes"
|
||||||
|
|
||||||
|
# Running containers
|
||||||
|
if [[ $running -gt 0 ]]; then
|
||||||
|
print_section "运行中的容器"
|
||||||
|
|
||||||
|
docker ps --format "{{.Names}}|{{.Image}}|{{.Status}}" 2>/dev/null | while IFS='|' read -r name image status; do
|
||||||
|
printf " ${C_OK}●${NC} ${C_VALUE}%-20s${NC} ${C_DIM}%-30s${NC} %s\n" "$name" "${image:0:30}" "$status"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# All containers
|
||||||
|
if [[ $containers -gt 0 ]]; then
|
||||||
|
print_section "所有容器"
|
||||||
|
|
||||||
|
docker ps -a --format "{{.Names}}|{{.Image}}|{{.Status}}" 2>/dev/null | head -20 | while IFS='|' read -r name image status; do
|
||||||
|
local icon="${C_DIM}○${NC}"
|
||||||
|
local color="$C_VALUE"
|
||||||
|
[[ "$status" == Up* ]] && icon="${C_OK}●${NC}" && color="$C_OK"
|
||||||
|
[[ "$status" == Exited* ]] && color="$C_DIM"
|
||||||
|
|
||||||
|
printf " ${icon} ${color}%-20s${NC} ${C_DIM}%-25s${NC} %s\n" "$name" "${image:0:25}" "$status"
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $containers -gt 20 ]] && echo -e " ${C_DIM}... 还有 $((containers - 20)) 个容器${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Images
|
||||||
|
if [[ $images -gt 0 ]]; then
|
||||||
|
print_section "镜像列表"
|
||||||
|
|
||||||
|
docker images --format "{{.Repository}}|{{.Tag}}|{{.Size}}|{{.CreatedAt}}" 2>/dev/null | grep -v "<none>" | head -25 | while IFS='|' read -r repo tag size created; do
|
||||||
|
printf " ${C_DIM}◆${NC} ${C_VALUE}%-35s${NC} ${C_DIM}%-10s %s${NC}\n" "${repo}:${tag}" "$size" "$created"
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $images -gt 25 ]] && echo -e " ${C_DIM}... 还有 $((images - 25)) 个镜像${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Networks
|
||||||
|
if [[ $networks -gt 0 ]]; then
|
||||||
|
print_section "网络"
|
||||||
|
|
||||||
|
docker network ls --format "{{.Name}}|{{.Driver}}|{{.Scope}}" 2>/dev/null | head -15 | while IFS='|' read -r name driver scope; do
|
||||||
|
printf " ${C_DIM}◎${NC} ${C_VALUE}%-20s${NC} ${C_DIM}[%s/%s]${NC}\n" "$name" "$driver" "$scope"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Volumes
|
||||||
|
if [[ $volumes -gt 0 ]]; then
|
||||||
|
print_section "存储卷"
|
||||||
|
|
||||||
|
docker volume ls --format "{{.Name}}|{{.Driver}}" 2>/dev/null | head -20 | while IFS='|' read -r name driver; do
|
||||||
|
printf " ${C_DIM}▪${NC} ${C_VALUE}%s${NC} ${C_DIM}(%s)${NC}\n" "$name" "$driver"
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ $volumes -gt 20 ]] && echo -e " ${C_DIM}... 还有 $((volumes - 20)) 个存储卷${NC}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Main
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
main() {
|
||||||
|
collect_server
|
||||||
|
collect_docker
|
||||||
|
echo -e "\n${C_HEADER}╔══════════════════════════════════════════════════════════════════════════════╗"
|
||||||
|
echo -e "${C_HEADER}║${C_OK} ✓ 信息收集完成 ${C_HEADER}║${NC}"
|
||||||
|
echo -e "${C_HEADER}╚══════════════════════════════════════════════════════════════════════════════╝${NC}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
145
filebrowser/install_filebrowser.sh
Normal file
145
filebrowser/install_filebrowser.sh
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Filebrowser 一键安装脚本
|
||||||
|
# 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/filebrowser/install_filebrowser.sh" | sudo bash
|
||||||
|
# 目录结构假设:
|
||||||
|
# BASE_URL/linux-amd64/filebrowser
|
||||||
|
# BASE_URL/linux-arm64/filebrowser
|
||||||
|
|
||||||
|
BASE_URL="https://pan.shumengya.top/d/scripts/filebrowser"
|
||||||
|
INSTALL_DIR="/shumengya/bin/filebrowser"
|
||||||
|
SERVICE_NAME="smy-filebrowser"
|
||||||
|
BINARY_NAME="filebrowser"
|
||||||
|
|
||||||
|
log() { printf '[Filebrowser-安装] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 进度条函数
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[Filebrowser-安装] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash install_filebrowser.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_arch() {
|
||||||
|
local machine
|
||||||
|
machine=$(uname -m)
|
||||||
|
case "$machine" in
|
||||||
|
x86_64|amd64) echo "linux-amd64" ;;
|
||||||
|
aarch64|arm64) echo "linux-arm64" ;;
|
||||||
|
*) fail "不支持的架构: $machine" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_install() {
|
||||||
|
local arch=$1
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
local bin_url="${BASE_URL}/${arch}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
log "正在处理 ${BINARY_NAME} ..."
|
||||||
|
|
||||||
|
# 停止旧服务
|
||||||
|
systemctl stop "${SERVICE_NAME}.service" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 强制清理可能残留的进程 (避免 Text file busy)
|
||||||
|
pids=$(pgrep -f "$INSTALL_DIR/$BINARY_NAME" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
log "清理残留进程..."
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 优先检测当前目录下是否有二进制文件(本地安装模式)
|
||||||
|
if [ -f "./${BINARY_NAME}" ]; then
|
||||||
|
log "发现本地文件 ./${BINARY_NAME},跳过下载..."
|
||||||
|
cp "./${BINARY_NAME}" "$tmp_dir/$BINARY_NAME"
|
||||||
|
else
|
||||||
|
# 下载二进制
|
||||||
|
curl -fsSL "$bin_url" -o "$tmp_dir/$BINARY_NAME" &
|
||||||
|
show_progress $! "下载二进制文件"
|
||||||
|
[ -f "$tmp_dir/$BINARY_NAME" ] || fail "下载失败"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 安装文件
|
||||||
|
cp "$tmp_dir/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
chmod +x "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
|
||||||
|
# 配置服务
|
||||||
|
write_service
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "${SERVICE_NAME}.service"
|
||||||
|
systemctl restart "${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}.service"; then
|
||||||
|
log "服务启动成功"
|
||||||
|
log "Filebrowser 监听端口: 2333"
|
||||||
|
log "配置文件和数据库将存储在: $INSTALL_DIR"
|
||||||
|
else
|
||||||
|
log "警告: 服务启动失败,查看日志:"
|
||||||
|
journalctl -u "${SERVICE_NAME}.service" --no-pager -n 10
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_service() {
|
||||||
|
local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
cat > "$service_file" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Filebrowser Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=$INSTALL_DIR
|
||||||
|
ExecStart=$INSTALL_DIR/$BINARY_NAME -r $INSTALL_DIR -d $INSTALL_DIR/filebrowser.db -l /var/log/filebrowser.log -p 2333 -a 0.0.0.0
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
local arch
|
||||||
|
arch=$(detect_arch)
|
||||||
|
log "检测到架构: $arch"
|
||||||
|
|
||||||
|
download_and_install "$arch"
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "安装任务完成!"
|
||||||
|
log "安装目录: $INSTALL_DIR"
|
||||||
|
log "服务名称: $SERVICE_NAME"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
BIN
filebrowser/linux-amd64/filebrowser
Normal file
BIN
filebrowser/linux-amd64/filebrowser
Normal file
Binary file not shown.
BIN
filebrowser/linux-arm64/filebrowser
Normal file
BIN
filebrowser/linux-arm64/filebrowser
Normal file
Binary file not shown.
0
filebrowser/smy-filebrowser.serivce
Normal file
0
filebrowser/smy-filebrowser.serivce
Normal file
109
filebrowser/uninstall_filebrowser.sh
Normal file
109
filebrowser/uninstall_filebrowser.sh
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Filebrowser 一键卸载脚本
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/filebrowser/uninstall_filebrowser.sh" | sudo bash
|
||||||
|
|
||||||
|
INSTALL_DIR="/shumengya/bin/filebrowser"
|
||||||
|
SERVICE_NAME="smy-filebrowser"
|
||||||
|
BINARY_NAME="filebrowser"
|
||||||
|
|
||||||
|
log() { printf '[Filebrowser-卸载] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[Filebrowser-卸载] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash uninstall_filebrowser.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_and_disable_service() {
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}.service"; then
|
||||||
|
systemctl stop "${SERVICE_NAME}.service"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-enabled --quiet "${SERVICE_NAME}.service" 2>/dev/null; then
|
||||||
|
systemctl disable "${SERVICE_NAME}.service"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_service_file() {
|
||||||
|
local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
if [ -f "$service_file" ]; then
|
||||||
|
rm -f "$service_file"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_process() {
|
||||||
|
# 查找并杀掉所有匹配的进程
|
||||||
|
local pids
|
||||||
|
pids=$(pgrep -f "$INSTALL_DIR/$BINARY_NAME" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_main() {
|
||||||
|
log "正在卸载 Filebrowser ..."
|
||||||
|
|
||||||
|
(stop_and_disable_service) &
|
||||||
|
show_progress $! "停止服务"
|
||||||
|
|
||||||
|
cleanup_process
|
||||||
|
|
||||||
|
(remove_service_file) &
|
||||||
|
show_progress $! "删除服务文件"
|
||||||
|
|
||||||
|
if [ -f "$INSTALL_DIR/$BINARY_NAME" ]; then
|
||||||
|
rm -f "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理日志文件(可选)
|
||||||
|
if [ -f "/var/log/filebrowser.log" ]; then
|
||||||
|
rm -f "/var/log/filebrowser.log"
|
||||||
|
log "已删除日志文件"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 询问是否删除数据(数据库)
|
||||||
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
|
# 检查目录下是否有除了二进制文件以外的内容(主要是数据库)
|
||||||
|
if [ -n "$(ls -A "$INSTALL_DIR" 2>/dev/null)" ]; then
|
||||||
|
log "警告: 安装目录 $INSTALL_DIR 中可能包含用户数据 (如 filebrowser.db)"
|
||||||
|
# 默认不自动删除数据目录,防止误删,提示用户手动删除
|
||||||
|
log "为了安全起见,脚本仅删除了二进制文件。若要彻底清除数据,请手动运行: rm -rf $INSTALL_DIR"
|
||||||
|
else
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
log "已删除空安装目录"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "卸载任务完成"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
uninstall_main
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
37
frp/frp/frpc.toml
Normal file
37
frp/frp/frpc.toml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#===========================================
|
||||||
|
#===================基础设置===================
|
||||||
|
#===========================================
|
||||||
|
|
||||||
|
|
||||||
|
serverAddr = "47.108.90.0"
|
||||||
|
serverPort = 7000
|
||||||
|
|
||||||
|
|
||||||
|
auth.method = "token"
|
||||||
|
auth.token = "smy"
|
||||||
|
|
||||||
|
|
||||||
|
webServer.addr = "0.0.0.0"
|
||||||
|
webServer.port = 7400
|
||||||
|
webServer.user = "shumengya"
|
||||||
|
webServer.password = "tyh@19900420"
|
||||||
|
webServer.pprofEnable = false
|
||||||
|
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
log.to = "console"
|
||||||
|
log.level = "info"
|
||||||
|
|
||||||
|
|
||||||
|
#===========================================
|
||||||
|
#===================Http服务===================
|
||||||
|
#===========================================
|
||||||
|
|
||||||
|
|
||||||
|
#大萌芽frp客户端-frpc
|
||||||
|
[[proxies]]
|
||||||
|
name = "frpc"
|
||||||
|
type = "http"
|
||||||
|
localIP = "127.0.0.1"
|
||||||
|
localPort = 7400
|
||||||
|
customDomains = ["frpc.shumengya.top"]
|
||||||
26
frp/frp/frps.toml
Normal file
26
frp/frp/frps.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# frp服务端配置 - 作为nginx后端服务
|
||||||
|
# 与nginx配合使用,提供HTTPS支持
|
||||||
|
|
||||||
|
|
||||||
|
bindAddr = "0.0.0.0"
|
||||||
|
bindPort = 7000
|
||||||
|
|
||||||
|
|
||||||
|
auth.method = "token"
|
||||||
|
auth.token = "smy"
|
||||||
|
|
||||||
|
|
||||||
|
# HTTP配置 - 作为后端服务(nginx代理)
|
||||||
|
vhostHTTPPort = 8080 # nginx代理到此端口
|
||||||
|
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
log.to = "console"
|
||||||
|
log.level = "info"
|
||||||
|
|
||||||
|
|
||||||
|
# 管理界面
|
||||||
|
webServer.addr = "0.0.0.0"
|
||||||
|
webServer.port = 7500
|
||||||
|
webServer.user = "shumengya"
|
||||||
|
webServer.password = "tyh@19900420"
|
||||||
BIN
frp/frp/linux_amd64/frpc
Normal file
BIN
frp/frp/linux_amd64/frpc
Normal file
Binary file not shown.
BIN
frp/frp/linux_amd64/frps
Normal file
BIN
frp/frp/linux_amd64/frps
Normal file
Binary file not shown.
BIN
frp/frp/linux_arm64/frpc
Normal file
BIN
frp/frp/linux_arm64/frpc
Normal file
Binary file not shown.
BIN
frp/frp/linux_arm64/frps
Normal file
BIN
frp/frp/linux_arm64/frps
Normal file
Binary file not shown.
0
frp/frp/smy-frpc.serivce
Normal file
0
frp/frp/smy-frpc.serivce
Normal file
0
frp/frp/smy-frps.serivce
Normal file
0
frp/frp/smy-frps.serivce
Normal file
192
frp/install_frp.sh
Normal file
192
frp/install_frp.sh
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# FRP 一键安装脚本
|
||||||
|
# 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/frp/install_frp.sh" | sudo bash
|
||||||
|
# 包含 frps (服务端) 和 frpc (客户端) 的安装
|
||||||
|
# 目录结构假设:
|
||||||
|
# BASE_URL/linux_amd64/frps
|
||||||
|
# BASE_URL/linux_amd64/frpc
|
||||||
|
# BASE_URL/frps.toml
|
||||||
|
# BASE_URL/frpc.toml
|
||||||
|
# BASE_URL/smy-frps.service (模板)
|
||||||
|
# BASE_URL/smy-frpc.service (模板)
|
||||||
|
|
||||||
|
BASE_URL="https://pan.shumengya.top/d/scripts/frp/frp"
|
||||||
|
INSTALL_DIR="/shumengya/bin/frp"
|
||||||
|
SERVICE_PREFIX="smy-frp"
|
||||||
|
|
||||||
|
log() { printf '[frp-安装] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 进度条函数
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[frp-安装] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash install_frp.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_arch() {
|
||||||
|
local machine
|
||||||
|
machine=$(uname -m)
|
||||||
|
case "$machine" in
|
||||||
|
x86_64|amd64) echo "linux_amd64" ;;
|
||||||
|
aarch64|arm64) echo "linux_arm64" ;;
|
||||||
|
*) fail "不支持的架构: $machine" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
select_mode() {
|
||||||
|
echo "请选择安装模式:" >&2
|
||||||
|
echo "1) 安装客户端 (frpc)" >&2
|
||||||
|
echo "2) 安装服务端 (frps)" >&2
|
||||||
|
echo "3) 同时安装" >&2
|
||||||
|
read -p "请输入选项 [1-3]: " choice < /dev/tty
|
||||||
|
case "$choice" in
|
||||||
|
1) echo "client" ;;
|
||||||
|
2) echo "server" ;;
|
||||||
|
3) echo "both" ;;
|
||||||
|
*) fail "无效选项" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_install() {
|
||||||
|
local mode=$1
|
||||||
|
local arch=$2
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
if [ "$mode" = "client" ] || [ "$mode" = "both" ]; then
|
||||||
|
install_component "frpc" "$arch" "$tmp_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mode" = "server" ] || [ "$mode" = "both" ]; then
|
||||||
|
install_component "frps" "$arch" "$tmp_dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_component() {
|
||||||
|
local name=$1 # frpc or frps
|
||||||
|
local arch=$2
|
||||||
|
local tmp_dir=$3
|
||||||
|
local bin_url="${BASE_URL}/${arch}/${name}"
|
||||||
|
local conf_url="${BASE_URL}/${name}.toml"
|
||||||
|
local service_name="${SERVICE_PREFIX}${name:3}" # smy-frpc or smy-frps
|
||||||
|
|
||||||
|
log "正在处理 $name ..."
|
||||||
|
|
||||||
|
# 停止旧服务
|
||||||
|
systemctl stop "${service_name}.service" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 强制清理可能残留的进程 (避免 Text file busy)
|
||||||
|
pids=$(pgrep -x "$name" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
log "清理 $name 残留进程..."
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 下载二进制
|
||||||
|
curl -fsSL "$bin_url" -o "$tmp_dir/$name" &
|
||||||
|
show_progress $! "下载 $name 二进制文件"
|
||||||
|
[ -f "$tmp_dir/$name" ] || fail "下载 $name 失败"
|
||||||
|
|
||||||
|
# 下载配置
|
||||||
|
curl -fsSL "$conf_url" -o "$tmp_dir/${name}.toml" &
|
||||||
|
show_progress $! "下载 $name 配置文件"
|
||||||
|
[ -f "$tmp_dir/${name}.toml" ] || fail "下载配置文件失败"
|
||||||
|
|
||||||
|
# 安装文件
|
||||||
|
cp "$tmp_dir/$name" "$INSTALL_DIR/$name"
|
||||||
|
chmod +x "$INSTALL_DIR/$name"
|
||||||
|
|
||||||
|
if [ ! -f "$INSTALL_DIR/${name}.toml" ]; then
|
||||||
|
cp "$tmp_dir/${name}.toml" "$INSTALL_DIR/${name}.toml"
|
||||||
|
else
|
||||||
|
log "保留现有配置文件: $INSTALL_DIR/${name}.toml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 配置服务
|
||||||
|
write_service "$name"
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "${service_name}.service"
|
||||||
|
systemctl restart "${service_name}.service"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet "${service_name}.service"; then
|
||||||
|
log "$name 服务启动成功"
|
||||||
|
else
|
||||||
|
log "警告: $name 服务启动失败,查看日志:"
|
||||||
|
journalctl -u "${service_name}.service" --no-pager -n 10
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_service() {
|
||||||
|
local name=$1
|
||||||
|
local service_name="${SERVICE_PREFIX}${name:3}"
|
||||||
|
local service_file="/etc/systemd/system/${service_name}.service"
|
||||||
|
|
||||||
|
cat > "$service_file" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=${service_name}
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=$INSTALL_DIR
|
||||||
|
ExecStart=$INSTALL_DIR/$name -c $INSTALL_DIR/${name}.toml
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
local arch
|
||||||
|
arch=$(detect_arch)
|
||||||
|
log "检测到架构: $arch"
|
||||||
|
|
||||||
|
local mode
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
case "$1" in
|
||||||
|
client|frpc) mode="client" ;;
|
||||||
|
server|frps) mode="server" ;;
|
||||||
|
both) mode="both" ;;
|
||||||
|
*) fail "无效参数: $1 (可用: client, server, both)" ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
mode=$(select_mode)
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_and_install "$mode" "$arch"
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "安装任务完成!"
|
||||||
|
log "安装目录: $INSTALL_DIR"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
66
frp/start_frp.sh
Normal file
66
frp/start_frp.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 恢复并启动 frp 服务
|
||||||
|
# 该脚本自动检测已安装的 frp 服务 (客户端/服务端) 并恢复其运行和开机自启
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/frp/start_frp.sh" | sudo bash
|
||||||
|
|
||||||
|
SERVICE_FRPC="smy-frpc"
|
||||||
|
SERVICE_FRPS="smy-frps"
|
||||||
|
|
||||||
|
log() { printf '[frp-启动] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 检查 root 权限
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行"
|
||||||
|
fi
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local svc=$1
|
||||||
|
local service_file="/etc/systemd/system/${svc}.service"
|
||||||
|
|
||||||
|
if [ -f "$service_file" ]; then
|
||||||
|
log "检测到已安装服务: $svc"
|
||||||
|
|
||||||
|
# 重置失败状态
|
||||||
|
systemctl reset-failed "$svc" 2>/dev/null || true
|
||||||
|
|
||||||
|
log "正在启用并启动 $svc..."
|
||||||
|
systemctl enable --now "$svc"
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
if systemctl is-active --quiet "$svc"; then
|
||||||
|
log "✔ $svc 启动成功 (状态: $(systemctl is-active "$svc"))"
|
||||||
|
else
|
||||||
|
log "✘ $svc 启动失败"
|
||||||
|
systemctl status "$svc" --no-pager -n 5 || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# 仅在调试时显示,避免干扰普通用户
|
||||||
|
# log "未检测到 $svc,跳过"
|
||||||
|
:
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log "正在扫描并恢复 frp 服务..."
|
||||||
|
|
||||||
|
found_any=false
|
||||||
|
if [ -f "/etc/systemd/system/${SERVICE_FRPC}.service" ]; then
|
||||||
|
start_service "$SERVICE_FRPC"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "/etc/systemd/system/${SERVICE_FRPS}.service" ]; then
|
||||||
|
start_service "$SERVICE_FRPS"
|
||||||
|
found_any=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$found_any" = false ]; then
|
||||||
|
log "未检测到任何已安装的 frp 服务 (smy-frpc 或 smy-frps)。"
|
||||||
|
log "请先运行安装脚本进行安装。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "操作完成。"
|
||||||
66
frp/stopkill_frp.sh
Normal file
66
frp/stopkill_frp.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 强制停止 frp 服务并清理进程
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/frp/stopkill_frp.sh" | sudo bash
|
||||||
|
|
||||||
|
SERVICE_FRPC="smy-frpc"
|
||||||
|
SERVICE_FRPS="smy-frps"
|
||||||
|
|
||||||
|
log() { printf '[frp-停止] %s\n' "$*" >&2; }
|
||||||
|
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
log "请使用 root 权限运行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
local svc=$1
|
||||||
|
if systemctl is-active --quiet "$svc"; then
|
||||||
|
log "正在停止服务 $svc..."
|
||||||
|
systemctl disable --now "$svc" 2>/dev/null || true
|
||||||
|
systemctl stop "$svc" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
# 即使服务不活跃,也要尝试 disable,防止开机自启
|
||||||
|
systemctl disable "$svc" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 重置失败状态
|
||||||
|
systemctl reset-failed "$svc" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_process() {
|
||||||
|
local name=$1
|
||||||
|
local pids
|
||||||
|
local current_pid=$$
|
||||||
|
|
||||||
|
# 尝试精确匹配
|
||||||
|
pids=$(pgrep -x "$name" || true)
|
||||||
|
|
||||||
|
# 如果没找到,尝试模糊匹配但排除当前脚本
|
||||||
|
if [ -z "$pids" ]; then
|
||||||
|
pids=$(pgrep -f "$name" | grep -v "$current_pid" || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
log "发现 $name 残留进程 PID: $pids,正在强制终止..."
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log "开始清理 frp 相关服务..."
|
||||||
|
|
||||||
|
# Stop Services
|
||||||
|
stop_service "$SERVICE_FRPC"
|
||||||
|
stop_service "$SERVICE_FRPS"
|
||||||
|
|
||||||
|
# Kill Processes
|
||||||
|
kill_process "frpc"
|
||||||
|
kill_process "frps"
|
||||||
|
|
||||||
|
log "正在验证服务状态..."
|
||||||
|
if pgrep -x "frpc" >/dev/null || pgrep -x "frps" >/dev/null; then
|
||||||
|
log "警告: 仍有 frp 进程在运行!"
|
||||||
|
ps -fp $(pgrep -x "frpc" "frps" 2>/dev/null) || true
|
||||||
|
else
|
||||||
|
log "frp 相关服务及进程已完全停止 (服务已禁用)。"
|
||||||
|
fi
|
||||||
145
frp/uninstall_frp.sh
Normal file
145
frp/uninstall_frp.sh
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# FRP 一键卸载脚本
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/frp/uninstall_frp.sh" | sudo bash
|
||||||
|
|
||||||
|
INSTALL_DIR="/shumengya/bin/frp"
|
||||||
|
SERVICE_PREFIX="smy-frp"
|
||||||
|
|
||||||
|
log() { printf '[frp-卸载] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[frp-卸载] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash uninstall_frp.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
select_mode() {
|
||||||
|
echo "请选择卸载模式:" >&2
|
||||||
|
echo "1) 卸载客户端 (frpc)" >&2
|
||||||
|
echo "2) 卸载服务端 (frps)" >&2
|
||||||
|
echo "3) 全部卸载" >&2
|
||||||
|
read -p "请输入选项 [1-3]: " choice < /dev/tty
|
||||||
|
case "$choice" in
|
||||||
|
1) echo "client" ;;
|
||||||
|
2) echo "server" ;;
|
||||||
|
3) echo "both" ;;
|
||||||
|
*) fail "无效选项" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_and_disable_service() {
|
||||||
|
local name=$1
|
||||||
|
local service_name="${SERVICE_PREFIX}${name:3}"
|
||||||
|
|
||||||
|
if systemctl is-active --quiet "${service_name}.service"; then
|
||||||
|
systemctl stop "${service_name}.service"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-enabled --quiet "${service_name}.service" 2>/dev/null; then
|
||||||
|
systemctl disable "${service_name}.service"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_service_file() {
|
||||||
|
local name=$1
|
||||||
|
local service_name="${SERVICE_PREFIX}${name:3}"
|
||||||
|
local service_file="/etc/systemd/system/${service_name}.service"
|
||||||
|
|
||||||
|
if [ -f "$service_file" ]; then
|
||||||
|
rm -f "$service_file"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_process() {
|
||||||
|
local name=$1
|
||||||
|
# 查找并杀掉所有匹配的进程
|
||||||
|
local pids
|
||||||
|
pids=$(pgrep -f "$INSTALL_DIR/$name" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_component() {
|
||||||
|
local name=$1
|
||||||
|
|
||||||
|
log "正在卸载 $name ..."
|
||||||
|
|
||||||
|
(stop_and_disable_service "$name") &
|
||||||
|
show_progress $! "停止服务"
|
||||||
|
|
||||||
|
cleanup_process "$name"
|
||||||
|
|
||||||
|
(remove_service_file "$name") &
|
||||||
|
show_progress $! "删除服务文件"
|
||||||
|
|
||||||
|
if [ -f "$INSTALL_DIR/$name" ]; then
|
||||||
|
rm -f "$INSTALL_DIR/$name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 注意:不自动删除配置文件,以免误删用户配置
|
||||||
|
# if [ -f "$INSTALL_DIR/${name}.toml" ]; then
|
||||||
|
# rm -f "$INSTALL_DIR/${name}.toml"
|
||||||
|
# fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
|
||||||
|
local mode
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
case "$1" in
|
||||||
|
client|frpc) mode="client" ;;
|
||||||
|
server|frps) mode="server" ;;
|
||||||
|
both) mode="both" ;;
|
||||||
|
*) fail "无效参数: $1 (可用: client, server, both)" ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
mode=$(select_mode)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mode" = "client" ] || [ "$mode" = "both" ]; then
|
||||||
|
uninstall_component "frpc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mode" = "server" ] || [ "$mode" = "both" ]; then
|
||||||
|
uninstall_component "frps"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 如果目录为空,则删除目录
|
||||||
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
|
if [ -z "$(ls -A "$INSTALL_DIR")" ]; then
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
log "已删除空安装目录"
|
||||||
|
else
|
||||||
|
log "保留安装目录 (包含配置文件): $INSTALL_DIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "卸载任务完成"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
267
login-info/login-info.sh
Executable file
267
login-info/login-info.sh
Executable file
@@ -0,0 +1,267 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/login_info.sh" | bash
|
||||||
|
|
||||||
|
# --- 精心选择的颜色定义 ---
|
||||||
|
NONE='\033[00m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
|
||||||
|
# 主题色 (可根据喜好调整)
|
||||||
|
# 可以选择一个主色调,例如蓝色系或绿色系
|
||||||
|
# 这里我们尝试用青色作为标题,黄色作为关键信息,白色作为次要信息
|
||||||
|
TITLE_COLOR='\033[01;36m' # 亮青色 (Bold Cyan)
|
||||||
|
INFO_COLOR='\033[01;33m' # 亮黄色 (Bold Yellow)
|
||||||
|
VALUE_COLOR='\033[00;37m' # 白色 (White)
|
||||||
|
LABEL_COLOR='\033[00;32m' # 绿色 (Green) - 用于标签
|
||||||
|
SUBTLE_COLOR='\033[01;30m' # 深灰色/亮黑色 (Bold Black / Dark Gray) - 用于分隔符或不重要信息
|
||||||
|
BLUE_COLOR='\033[01;34m' # 亮蓝色 (Bold Blue)
|
||||||
|
ORANGE_COLOR='\033[01;33m' # 亮黄色 (在16色终端中常作为橙色) - 使用 INFO_COLOR 相同的代码
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- 设备名称和欢迎 ---
|
||||||
|
HOSTNAME_TEXT=$(hostname) # 如果不再需要显示hostname,可以注释掉或删除这行
|
||||||
|
|
||||||
|
# --- 系统概览 ---
|
||||||
|
OS_INFO=$(lsb_release -ds 2>/dev/null || echo "N/A")
|
||||||
|
KERNEL_VERSION=$(uname -sr 2>/dev/null || echo "N/A")
|
||||||
|
BOOT_TIME=$(uptime -s 2>/dev/null || who -b | awk '{print $3, $4}')
|
||||||
|
UPTIME=$(uptime -p 2>/dev/null | sed 's/up //')
|
||||||
|
LOAD_AVG=$(uptime 2>/dev/null | awk -F'load average: ' '{print $2}')
|
||||||
|
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
|
echo -e "${TITLE_COLOR}${BOLD}系统概览${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}系统名称:${NONE} ${VALUE_COLOR}$(uname -a)${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}操作系统:${NONE} ${VALUE_COLOR}$OS_INFO${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}内核版本:${NONE} ${VALUE_COLOR}$KERNEL_VERSION${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}启动时间:${NONE} ${VALUE_COLOR}$BOOT_TIME${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}运行时间:${NONE} ${VALUE_COLOR}$UPTIME${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}平均负载:${NONE} ${VALUE_COLOR}$LOAD_AVG${NONE}"
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- 硬件资源 ---
|
||||||
|
# 获取 CPU 型号,处理可能的多行输出,只取第一行,并去除首尾空格
|
||||||
|
CPU_MODEL=$(grep "model name" /proc/cpuinfo | head -n 1 | awk -F ':' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//' || echo "N/A")
|
||||||
|
CPU_CORES=$(grep -c "processor" /proc/cpuinfo 2>/dev/null || echo "N/A")
|
||||||
|
# CPU 温度 (尽可能兼容更多机型)
|
||||||
|
CPU_TEMP=""
|
||||||
|
# 定义获取CPU温度的函数
|
||||||
|
get_cpu_temp() {
|
||||||
|
local temp_c=""
|
||||||
|
|
||||||
|
# 1. 尝试使用 sensors 命令 (如果安装了 lm-sensors)
|
||||||
|
if command -v sensors &> /dev/null; then
|
||||||
|
# 尝试匹配常见的CPU温度标签
|
||||||
|
# Package id 0: Intel/AMD 多核封装温度
|
||||||
|
# Tctl/Tdie: AMD Ryzen
|
||||||
|
# Core 0: Intel Core
|
||||||
|
# cpu_thermal: 树莓派/通用
|
||||||
|
# Composite: 部分嵌入式设备
|
||||||
|
temp_c=$(sensors | grep -iE '^(package id 0|tctl|tdie|core 0|cpu_thermal|composite|temp1):' | head -n 1 | awk '{print $2}' | tr -d '+°C')
|
||||||
|
if [ -n "$temp_c" ]; then
|
||||||
|
echo "$temp_c"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. 尝试读取 /sys/class/thermal/thermal_zone*
|
||||||
|
# 优先查找 x86_pkg_temp (Intel) 或 k10temp (AMD) 或 cpu-thermal (ARM) 或 acpitz
|
||||||
|
for zone in /sys/class/thermal/thermal_zone*; do
|
||||||
|
[ -e "$zone/type" ] || continue
|
||||||
|
type=$(cat "$zone/type")
|
||||||
|
if [[ "$type" == "x86_pkg_temp" || "$type" == "k10temp" || "$type" == "cpu-thermal" || "$type" == "acpitz" ]]; then
|
||||||
|
if [ -r "$zone/temp" ]; then
|
||||||
|
temp_raw=$(cat "$zone/temp")
|
||||||
|
# 转换为摄氏度 (除以1000)
|
||||||
|
if [ "$temp_raw" -gt 0 ] 2>/dev/null; then
|
||||||
|
awk "BEGIN {printf \"%.1f\", $temp_raw/1000}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 3. 如果还没找到,尝试任何 thermal_zone0 (通常是主要的)
|
||||||
|
if [ -r "/sys/class/thermal/thermal_zone0/temp" ]; then
|
||||||
|
temp_raw=$(cat "/sys/class/thermal/thermal_zone0/temp")
|
||||||
|
if [ "$temp_raw" -gt 0 ] 2>/dev/null; then
|
||||||
|
awk "BEGIN {printf \"%.1f\", $temp_raw/1000}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
CPU_TEMP_VAL=$(get_cpu_temp)
|
||||||
|
if [ -n "$CPU_TEMP_VAL" ]; then
|
||||||
|
CPU_TEMP="(${INFO_COLOR}${CPU_TEMP_VAL}°C${VALUE_COLOR})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 智能内存显示:小于 1GB 显示 MB,否则显示 GB (保留1位小数)
|
||||||
|
MEM_TOTAL_MB=$(free -m | awk 'NR==2{print $2}')
|
||||||
|
if [ "$MEM_TOTAL_MB" -ge 1000 ]; then
|
||||||
|
MEM_TOTAL_DISPLAY=$(awk -v val="$MEM_TOTAL_MB" 'BEGIN {printf "%.1fGB", val/1024}')
|
||||||
|
else
|
||||||
|
MEM_TOTAL_DISPLAY="${MEM_TOTAL_MB}MB"
|
||||||
|
fi
|
||||||
|
|
||||||
|
MEM_USED_PERCENT=$(free -m | awk 'NR==2{printf "%.1f%%", $3*100/$2 }')
|
||||||
|
MEM_AVAILABLE_MB=$(free -m | awk 'NR==2{print $7"MB"}') # 'available' 通常比 'free' 更能代表实际可用内存
|
||||||
|
|
||||||
|
# --- GPU 检测 ---
|
||||||
|
# 优先使用 lspci 检测 VGA, 3D, Display 控制器
|
||||||
|
GPU_INFO=""
|
||||||
|
if command -v lspci &> /dev/null; then
|
||||||
|
# 获取设备列表,去除前面的地址信息,只保留设备名称
|
||||||
|
# 典型 lspci 输出: "00:02.0 VGA compatible controller: Intel Corporation ..."
|
||||||
|
# 我们需要截取 "Intel Corporation ..."
|
||||||
|
# 使用 sed 处理:匹配开头到第二个冒号之前的所有内容并删除
|
||||||
|
GPU_INFO=$(lspci | grep -iE 'vga|3d|display' | sed 's/^.*: .*[:] //')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${TITLE_COLOR}${BOLD}硬件资源${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}中央处理器 (CPU):${NONE}"
|
||||||
|
echo -e " ${VALUE_COLOR}$CPU_MODEL - ${CPU_CORES} 核心 ${CPU_TEMP}${NONE}"
|
||||||
|
if [ -n "$GPU_INFO" ]; then
|
||||||
|
echo -e " ${LABEL_COLOR}图形处理器 (GPU):${NONE}"
|
||||||
|
# 处理多显卡情况,逐行显示
|
||||||
|
echo "$GPU_INFO" | while read -r line; do
|
||||||
|
echo -e " ${VALUE_COLOR}$line${NONE}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo -e " ${LABEL_COLOR}内存 (RAM):${NONE}"
|
||||||
|
echo -e " ${VALUE_COLOR}总计: ${INFO_COLOR}$MEM_TOTAL_DISPLAY${VALUE_COLOR} | 已用: ${INFO_COLOR}$MEM_USED_PERCENT${VALUE_COLOR} | 可用: ${INFO_COLOR}$MEM_AVAILABLE_MB${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}存储空间:${NONE}"
|
||||||
|
|
||||||
|
# --- 修改的存储部分 (删除进度条, 修改颜色) ---
|
||||||
|
# 使用 df -hT 获取信息,-x 排除不必要的类型 (保留 overlay)
|
||||||
|
# 使用 awk 跳过头部 (NR>1),并按列提取 (Filesystem, Type, Size, Used, Use%, Mounted on)
|
||||||
|
# 注意:已添加 -x overlay 以隐藏 Docker 挂载卷
|
||||||
|
df -hT -x tmpfs -x devtmpfs -x devpts -x proc -x sysfs -x cgroup -x fusectl -x securityfs -x pstore -x efivarfs -x autofs -x overlay 2>/dev/null | awk 'NR>1 {print $1, $2, $3, $4, $6, $7}' | while read -r DEVICE TYPE SIZE USED PERCENT MOUNTPOINT; do
|
||||||
|
|
||||||
|
# --- 已删除进度条的计算和绘制 ---
|
||||||
|
|
||||||
|
echo -e " ${INFO_COLOR}${MOUNTPOINT}${NONE} (${VALUE_COLOR}${DEVICE} - ${TYPE}${NONE})"
|
||||||
|
# --- 核心改动:将 总 和 已用 后面的数值改为蓝色 ---
|
||||||
|
# LABEL_COLOR 用于标签文字 "总:" 和 "已用:"
|
||||||
|
# BLUE_COLOR 用于数值 SIZE 和 USED
|
||||||
|
# VALUE_COLOR 用于百分比 PERCENT 以及括号和逗号
|
||||||
|
echo -e " ${LABEL_COLOR}总:${NONE} ${BLUE_COLOR}${SIZE}${NONE}, ${LABEL_COLOR}已用:${NONE} ${BLUE_COLOR}${USED}${NONE} (${VALUE_COLOR}${PERCENT}${NONE})"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- 网络连接 ---
|
||||||
|
echo -e "${TITLE_COLOR}${BOLD}网络连接${NONE}"
|
||||||
|
INTERFACES_UP=$(ip -o link show up | awk -F': ' '{print $2}' | cut -d '@' -f 1) # 使用 cut 移除 @ifname 后缀
|
||||||
|
HAS_IP=false
|
||||||
|
if [ -n "$INTERFACES_UP" ]; then
|
||||||
|
for IFACE in $INTERFACES_UP; do
|
||||||
|
if [[ "$IFACE" == "lo" ]]; then continue; fi # 跳过 lo 接口
|
||||||
|
# 跳过 Docker 相关接口
|
||||||
|
if [[ "$IFACE" == docker* || "$IFACE" == veth* || "$IFACE" == br-* ]]; then continue; fi
|
||||||
|
|
||||||
|
IP_ADDRS=$(ip -4 addr show dev "$IFACE" 2>/dev/null | grep -oP 'inet \K[\d.]+' | tr '\n' ' ')
|
||||||
|
IP_ADDRS_V6=$(ip -6 addr show dev "$IFACE" 2>/dev/null | grep -oP 'inet6 \K[0-9a-fA-F:]+' | grep -ivE '^fe80::' | head -n1 | tr '\n' ' ') # 只显示一个全局 IPv6
|
||||||
|
MAC_ADDR=$(ip link show dev "$IFACE" 2>/dev/null | awk '/ether/ {print $2}')
|
||||||
|
|
||||||
|
if [ -n "$IP_ADDRS" ] || [ -n "$IP_ADDRS_V6" ]; then
|
||||||
|
HAS_IP=true
|
||||||
|
IFACE_TYPE_LABEL=""
|
||||||
|
if [[ "$IFACE" == wlan* || "$IFACE" == wlp* || "$IFACE" == ath* || "$IFACE" == ra* ]]; then
|
||||||
|
IFACE_TYPE_LABEL="(${LABEL_COLOR}WiFi${NONE}) "
|
||||||
|
elif [[ "$IFACE" == eth* || "$IFACE" == enp* || "$IFACE" == eno* ]]; then
|
||||||
|
IFACE_TYPE_LABEL="(${LABEL_COLOR}有线${NONE}) "
|
||||||
|
# 添加对虚拟接口类型的更全面判断
|
||||||
|
elif [[ "$IFACE" == docker* || "$IFACE" == br-* || "$IFACE" == veth* || "$IFACE" == tun* || "$IFACE" == tap* || "$IFACE" == virbr* ]]; then
|
||||||
|
IFACE_TYPE_LABEL="(${LABEL_COLOR}虚拟${NONE}) "
|
||||||
|
# 考虑桥接接口
|
||||||
|
elif ip link show "$IFACE" | grep -q "bridge"; then
|
||||||
|
IFACE_TYPE_LABEL="(${LABEL_COLOR}桥接${NONE}) "
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo -e " ${INFO_COLOR}${BOLD}${IFACE}${NONE} ${IFACE_TYPE_LABEL}"
|
||||||
|
[ -n "$IP_ADDRS" ] && echo -e " ${LABEL_COLOR}IPv4:${NONE} ${VALUE_COLOR}${IP_ADDRS% }${NONE}" # % 移除末尾空格
|
||||||
|
[ -n "$IP_ADDRS_V6" ] && echo -e " ${LABEL_COLOR}IPv6:${NONE} ${VALUE_COLOR}${IP_ADDRS_V6% }${NONE}"
|
||||||
|
[ -n "$MAC_ADDR" ] && echo -e " ${LABEL_COLOR}MAC:${NONE} ${VALUE_COLOR}${MAC_ADDR}${NONE}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if ! $HAS_IP; then
|
||||||
|
echo -e " ${VALUE_COLOR}未检测到活动的网络连接或IP地址${NONE}"
|
||||||
|
fi
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- Docker 容器 ---
|
||||||
|
# 检查 docker 命令是否存在且守护进程正在运行
|
||||||
|
if command -v docker &> /dev/null && docker ps &> /dev/null; then
|
||||||
|
DOCKER_RUNNING_COUNT=$(docker ps -q | wc -l)
|
||||||
|
DOCKER_TOTAL_COUNT=$(docker ps -aq | wc -l)
|
||||||
|
|
||||||
|
echo -e "${TITLE_COLOR}${BOLD}Docker 容器${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}容器数量:${NONE} ${VALUE_COLOR}运行中: ${INFO_COLOR}${DOCKER_RUNNING_COUNT}${VALUE_COLOR} / 总计: ${INFO_COLOR}${DOCKER_TOTAL_COUNT}${NONE}"
|
||||||
|
|
||||||
|
if [ "$DOCKER_RUNNING_COUNT" -gt 0 ]; then
|
||||||
|
# 获取名称列表,用逗号分隔
|
||||||
|
DOCKER_NAMES=$(docker ps --format "{{.Names}}" | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /g')
|
||||||
|
echo -e " ${LABEL_COLOR}运行列表:${NONE} ${VALUE_COLOR}${DOCKER_NAMES}${NONE}"
|
||||||
|
fi
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- 活动与统计 ---
|
||||||
|
LAST_LOGIN_INFO=$(last -n 1 -wFai 2>/dev/null | head -n 1) # -a 把hostname放最后, -i 显示IP
|
||||||
|
LAST_LOGIN_DISPLAY=""
|
||||||
|
if [[ "$LAST_LOGIN_INFO" == *"wtmp begins"* || -z "$LAST_LOGIN_INFO" || $(echo "$LAST_LOGIN_INFO" | wc -w) -lt 5 ]]; then # 增加字段数检查
|
||||||
|
LAST_LOGIN_DISPLAY="${VALUE_COLOR}无先前登录记录或记录格式异常${NONE}"
|
||||||
|
else
|
||||||
|
# --- 修改的上次登录部分 ---
|
||||||
|
USER=$(echo "$LAST_LOGIN_INFO" | awk '{print $1}')
|
||||||
|
# 对于 -wFai, IP/hostname 通常是第三列
|
||||||
|
FROM_IP_OR_HOST=$(echo "$LAST_LOGIN_INFO" | awk '{print $3}')
|
||||||
|
|
||||||
|
# 精确提取日期和时间部分,跳过 'still' 和年份 (字段 4到7)
|
||||||
|
LOGIN_TIME_FIELDS=$(echo "$LAST_LOGIN_INFO" | awk '{print $4, $5, $6, $7}')
|
||||||
|
|
||||||
|
# 尝试用date格式化,如果失败则显示原始字段
|
||||||
|
LOGIN_TIME_FORMATTED=$(date -d "$LOGIN_TIME_FIELDS $(date +%Y)" +"%Y-%m-%d %H:%M:%S" 2>/dev/null) # 显式添加年份尝试格式化
|
||||||
|
if [ -z "$LOGIN_TIME_FORMATTED" ]; then
|
||||||
|
LOGIN_TIME_FORMATTED="${VALUE_COLOR}${LOGIN_TIME_FIELDS}${NONE} (raw)" # 格式化失败,显示原始字段并标记
|
||||||
|
fi
|
||||||
|
|
||||||
|
LAST_LOGIN_DISPLAY="${LABEL_COLOR}用户:${NONE} ${INFO_COLOR}$USER${NONE}, ${LABEL_COLOR}来自:${NONE} ${INFO_COLOR}$FROM_IP_OR_HOST${NONE}, ${LABEL_COLOR}时间:${NONE} ${VALUE_COLOR}$LOGIN_TIME_FORMATTED${NONE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
PACKAGE_COUNT=$(dpkg-query -f '${Package}\n' -W 2>/dev/null | wc -l || echo "N/A") # Debian/APT
|
||||||
|
|
||||||
|
echo -e "${TITLE_COLOR}${BOLD}活动与统计${NONE}"
|
||||||
|
echo -e " ${LABEL_COLOR}上次登录:${NONE} $LAST_LOGIN_DISPLAY"
|
||||||
|
|
||||||
|
# 检查是否是 Debian/Ubuntu 系列系统,然后显示包数
|
||||||
|
if command -v dpkg-query &> /dev/null; then
|
||||||
|
PACKAGE_COUNT=$(dpkg-query -f '${Package}\n' -W 2>/dev/null | wc -l || echo "N/A")
|
||||||
|
echo -e " ${LABEL_COLOR}软件包数:${NONE} ${VALUE_COLOR}$PACKAGE_COUNT (Debian/APT)${NONE}"
|
||||||
|
elif command -v rpm &> /dev/null; then
|
||||||
|
PACKAGE_COUNT=$(rpm -qa 2>/dev/null | wc -l || echo "N/A")
|
||||||
|
echo -e " ${LABEL_COLOR}软件包数:${NONE} ${VALUE_COLOR}$PACKAGE_COUNT (RPM/Yum/Dnf)${NONE}"
|
||||||
|
elif command -v pacman &> /dev/null; then
|
||||||
|
PACKAGE_COUNT=$(pacman -Qq 2>/dev/null | wc -l || echo "N/A")
|
||||||
|
echo -e " ${LABEL_COLOR}软件包数:${NONE} ${VALUE_COLOR}$PACKAGE_COUNT (Pacman)${NONE}"
|
||||||
|
else
|
||||||
|
echo -e " ${LABEL_COLOR}软件包数:${NONE} ${VALUE_COLOR}N/A (未知包管理器)${NONE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT_DATETIME=$(date +"%Y年%m月%d日 %A %H:%M:%S %Z")
|
||||||
|
echo -e " ${LABEL_COLOR}当前登录时间:${NONE} ${VALUE_COLOR}$CURRENT_DATETIME$"
|
||||||
|
|
||||||
|
echo -e "${SUBTLE_COLOR}───────────────────────────────────────────────────────────────────────────────${NONE}"
|
||||||
143
mengyamonitor/install_mengyamonitor.sh
Normal file
143
mengyamonitor/install_mengyamonitor.sh
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# MengyaMonitor 一键安装脚本
|
||||||
|
# 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/mengyamonitor/install_mengyamonitor.sh" | sudo bash
|
||||||
|
# 目录结构假设:
|
||||||
|
# BASE_URL/linux_amd64/mengyamonitor-backend
|
||||||
|
# BASE_URL/linux_arm64/mengyamonitor-backend
|
||||||
|
|
||||||
|
BASE_URL="https://pan.shumengya.top/d/scripts/mengyamonitor"
|
||||||
|
INSTALL_DIR="/shumengya/bin/mengyamonitor"
|
||||||
|
SERVICE_NAME="smy-mengyamonitor"
|
||||||
|
BINARY_NAME="mengyamonitor-backend"
|
||||||
|
|
||||||
|
log() { printf '[MengyaMonitor-安装] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 进度条函数
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[MengyaMonitor-安装] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash install_mengyamonitor.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_arch() {
|
||||||
|
local machine
|
||||||
|
machine=$(uname -m)
|
||||||
|
case "$machine" in
|
||||||
|
x86_64|amd64) echo "linux_amd64" ;;
|
||||||
|
aarch64|arm64) echo "linux_arm64" ;;
|
||||||
|
*) fail "不支持的架构: $machine" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_and_install() {
|
||||||
|
local arch=$1
|
||||||
|
local tmp_dir
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
local bin_url="${BASE_URL}/${arch}/${BINARY_NAME}"
|
||||||
|
|
||||||
|
log "正在处理 ${BINARY_NAME} ..."
|
||||||
|
|
||||||
|
# 停止旧服务
|
||||||
|
systemctl stop "${SERVICE_NAME}.service" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 强制清理可能残留的进程 (避免 Text file busy)
|
||||||
|
pids=$(pgrep -f "$BINARY_NAME" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
log "清理残留进程..."
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 优先检测当前目录下是否有二进制文件(本地安装模式)
|
||||||
|
if [ -f "./${BINARY_NAME}" ]; then
|
||||||
|
log "发现本地文件 ./${BINARY_NAME},跳过下载..."
|
||||||
|
cp "./${BINARY_NAME}" "$tmp_dir/$BINARY_NAME"
|
||||||
|
else
|
||||||
|
# 下载二进制
|
||||||
|
curl -fsSL "$bin_url" -o "$tmp_dir/$BINARY_NAME" &
|
||||||
|
show_progress $! "下载二进制文件"
|
||||||
|
[ -f "$tmp_dir/$BINARY_NAME" ] || fail "下载失败"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 安装文件
|
||||||
|
cp "$tmp_dir/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
chmod +x "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
|
||||||
|
# 配置服务
|
||||||
|
write_service
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "${SERVICE_NAME}.service"
|
||||||
|
systemctl restart "${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}.service"; then
|
||||||
|
log "服务启动成功"
|
||||||
|
else
|
||||||
|
log "警告: 服务启动失败,查看日志:"
|
||||||
|
journalctl -u "${SERVICE_NAME}.service" --no-pager -n 10
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_service() {
|
||||||
|
local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
cat > "$service_file" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MengyaMonitor Backend Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=$INSTALL_DIR
|
||||||
|
ExecStart=$INSTALL_DIR/$BINARY_NAME
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=append:/var/log/mengyamonitor.log
|
||||||
|
StandardError=append:/var/log/mengyamonitor.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
local arch
|
||||||
|
arch=$(detect_arch)
|
||||||
|
log "检测到架构: $arch"
|
||||||
|
|
||||||
|
download_and_install "$arch"
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "安装任务完成!"
|
||||||
|
log "安装目录: $INSTALL_DIR"
|
||||||
|
log "服务名称: $SERVICE_NAME"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
BIN
mengyamonitor/linux_amd64/mengyamonitor-backend
Normal file
BIN
mengyamonitor/linux_amd64/mengyamonitor-backend
Normal file
Binary file not shown.
BIN
mengyamonitor/linux_arm64/mengyamonitor-backend
Normal file
BIN
mengyamonitor/linux_arm64/mengyamonitor-backend
Normal file
Binary file not shown.
15
mengyamonitor/smy-mengyamonitor.service
Normal file
15
mengyamonitor/smy-mengyamonitor.service
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=MengyaMonitor Backend Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/shumengya/bin/mengyamonitor
|
||||||
|
ExecStart=/shumengya/bin/mengyamonitor/mengyamonitor-backend
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=append:/var/log/mengyamonitor.log
|
||||||
|
StandardError=append:/var/log/mengyamonitor.log
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
102
mengyamonitor/uninstall_mengyamonitor.sh
Normal file
102
mengyamonitor/uninstall_mengyamonitor.sh
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# MengyaMonitor 一键卸载脚本
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/mengyamonitor/uninstall_mengyamonitor.sh" | sudo bash
|
||||||
|
|
||||||
|
INSTALL_DIR="/shumengya/bin/mengyamonitor"
|
||||||
|
SERVICE_NAME="smy-mengyamonitor"
|
||||||
|
BINARY_NAME="mengyamonitor-backend"
|
||||||
|
|
||||||
|
log() { printf '[MengyaMonitor-卸载] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[MengyaMonitor-卸载] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash uninstall_mengyamonitor.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_and_disable_service() {
|
||||||
|
if systemctl is-active --quiet "${SERVICE_NAME}.service"; then
|
||||||
|
systemctl stop "${SERVICE_NAME}.service"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-enabled --quiet "${SERVICE_NAME}.service" 2>/dev/null; then
|
||||||
|
systemctl disable "${SERVICE_NAME}.service"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_service_file() {
|
||||||
|
local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
if [ -f "$service_file" ]; then
|
||||||
|
rm -f "$service_file"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_process() {
|
||||||
|
# 查找并杀掉所有匹配的进程
|
||||||
|
local pids
|
||||||
|
pids=$(pgrep -f "$BINARY_NAME" || true)
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_main() {
|
||||||
|
log "正在卸载 MengyaMonitor ..."
|
||||||
|
|
||||||
|
(stop_and_disable_service) &
|
||||||
|
show_progress $! "停止服务"
|
||||||
|
|
||||||
|
cleanup_process
|
||||||
|
|
||||||
|
(remove_service_file) &
|
||||||
|
show_progress $! "删除服务文件"
|
||||||
|
|
||||||
|
if [ -f "$INSTALL_DIR/$BINARY_NAME" ]; then
|
||||||
|
rm -f "$INSTALL_DIR/$BINARY_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理日志文件(可选,这里默认清理)
|
||||||
|
if [ -f "/var/log/mengyamonitor.log" ]; then
|
||||||
|
rm -f "/var/log/mengyamonitor.log"
|
||||||
|
log "已删除日志文件"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 如果目录为空,则删除目录
|
||||||
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
log "已删除安装目录"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "卸载任务完成"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
uninstall_main
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
182
openlist/install_openlist.sh
Normal file
182
openlist/install_openlist.sh
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# openlist 一键安装脚本
|
||||||
|
# 用法示例:curl -fsSL "https://pan.shumengya.top/d/scripts/openlist/install_openlist.sh" | bash
|
||||||
|
|
||||||
|
BASE_URL="https://pan.shumengya.top/d/scripts/openlist/openlist"
|
||||||
|
INSTALL_DIR="/shumengya/bin/openlist"
|
||||||
|
SERVICE_NAME="smy-openlist"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
log() { printf '[openlist-安装] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 进度条函数
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[openlist-安装] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
check_port() {
|
||||||
|
# 检查端口占用并尝试清理
|
||||||
|
local port=5244
|
||||||
|
if lsof -i :$port >/dev/null 2>&1 || netstat -tunlp | grep -q ":$port "; then
|
||||||
|
log "检测到端口 $port 被占用,尝试清理..."
|
||||||
|
fuser -k -n tcp $port >/dev/null 2>&1 || true
|
||||||
|
sleep 1
|
||||||
|
if lsof -i :$port >/dev/null 2>&1; then
|
||||||
|
# 尝试查找占用进程并强制杀掉
|
||||||
|
local pid
|
||||||
|
pid=$(lsof -t -i:$port 2>/dev/null || true)
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
# 必须使用 root 权限执行
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash install_openlist.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_arch() {
|
||||||
|
# 自动检测 CPU 架构,选择对应二进制
|
||||||
|
local machine
|
||||||
|
machine=$(uname -m)
|
||||||
|
case "$machine" in
|
||||||
|
x86_64|amd64) echo "amd64" ;;
|
||||||
|
aarch64|arm64) echo "arm64" ;;
|
||||||
|
*) fail "不支持的架构: $machine" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
download_files() {
|
||||||
|
# 下载对应架构的二进制包和 data 目录
|
||||||
|
local arch tmp_dir binary_url data_url
|
||||||
|
arch="$1"
|
||||||
|
tmp_dir=$(mktemp -d)
|
||||||
|
# 只在 tmp_dir 存在时清理
|
||||||
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi' EXIT
|
||||||
|
|
||||||
|
binary_url="${BASE_URL}/linux-${arch}.tar.gz"
|
||||||
|
data_url="${BASE_URL}/data.tgz"
|
||||||
|
|
||||||
|
# 后台下载并显示进度
|
||||||
|
curl -fsSL "$binary_url" -o "$tmp_dir/openlist.tar.gz" &
|
||||||
|
show_progress $! "正在下载 ${arch} 架构的二进制文件"
|
||||||
|
[ -f "$tmp_dir/openlist.tar.gz" ] || fail "下载二进制文件失败"
|
||||||
|
|
||||||
|
# 检查 data 目录是否已存在,如果存在则跳过下载
|
||||||
|
if [ -d "$INSTALL_DIR/data" ]; then
|
||||||
|
log "检测到已存在 data 目录,跳过下载以保护现有数据"
|
||||||
|
else
|
||||||
|
curl -fsSL "$data_url" -o "$tmp_dir/data.tgz" &
|
||||||
|
show_progress $! "正在下载 data 配置文件"
|
||||||
|
[ -f "$tmp_dir/data.tgz" ] || fail "下载 data 文件失败"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$tmp_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_binary_and_data() {
|
||||||
|
# 解压并安装二进制文件和 data 目录
|
||||||
|
local tmp_dir extracted_dir bin_path
|
||||||
|
tmp_dir="$1"
|
||||||
|
|
||||||
|
log "正在安装 openlist 到 $INSTALL_DIR..."
|
||||||
|
|
||||||
|
# 尝试停止服务
|
||||||
|
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
# 检查并清理端口
|
||||||
|
check_port
|
||||||
|
|
||||||
|
mkdir -p "$INSTALL_DIR"
|
||||||
|
|
||||||
|
# 只在下载了 data.tgz 时才解压
|
||||||
|
if [ -f "$tmp_dir/data.tgz" ]; then
|
||||||
|
tar -xzf "$tmp_dir/data.tgz" -C "$INSTALL_DIR/" || fail "解压 data 目录失败"
|
||||||
|
else
|
||||||
|
log "保留现有 data 目录,不进行覆盖"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 解压二进制文件
|
||||||
|
extracted_dir=$(mktemp -d)
|
||||||
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi; if [ -n "${extracted_dir:-}" ] && [ -d "$extracted_dir" ]; then rm -rf "$extracted_dir"; fi' EXIT
|
||||||
|
tar -xzf "$tmp_dir/openlist.tar.gz" -C "$extracted_dir" || fail "解压二进制文件失败"
|
||||||
|
bin_path=$(find "$extracted_dir" -maxdepth 2 -type f -name "openlist" -perm -111 | head -n 1)
|
||||||
|
[ -n "$bin_path" ] || fail "未找到 openlist 可执行文件"
|
||||||
|
cp "$bin_path" "$INSTALL_DIR/openlist" || fail "复制可执行文件失败"
|
||||||
|
chmod +x "$INSTALL_DIR/openlist"
|
||||||
|
log "文件安装完成"
|
||||||
|
}
|
||||||
|
|
||||||
|
write_service() {
|
||||||
|
# 写入 systemd 服务文件,默认自启动并在失败时重启
|
||||||
|
log "正在配置 systemd 服务..."
|
||||||
|
cat > "$SERVICE_FILE" <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=openlist
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/shumengya/bin/openlist
|
||||||
|
ExecStart=/shumengya/bin/openlist/openlist server
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_service() {
|
||||||
|
# 重新加载 systemd,设置开机自启并立即启动
|
||||||
|
log "正在启用并启动服务..."
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "$SERVICE_NAME"
|
||||||
|
systemctl restart "$SERVICE_NAME"
|
||||||
|
|
||||||
|
# 等待几秒钟以捕获立即崩溃的情况
|
||||||
|
sleep 3
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
local arch tmp_dir=""
|
||||||
|
arch=$(detect_arch)
|
||||||
|
log "检测到系统架构: $arch"
|
||||||
|
tmp_dir=$(download_files "$arch")
|
||||||
|
install_binary_and_data "$tmp_dir"
|
||||||
|
write_service
|
||||||
|
enable_service
|
||||||
|
log "=========================================="
|
||||||
|
log "安装完成! openlist 已安装到: $INSTALL_DIR"
|
||||||
|
log "服务已启动,查看状态: systemctl status $SERVICE_NAME"
|
||||||
|
|
||||||
|
# 检查服务状态
|
||||||
|
if ! systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
log "警告: 服务未能正常启动"
|
||||||
|
journalctl -u "$SERVICE_NAME" --no-pager -n 20
|
||||||
|
fi
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
BIN
openlist/openlist/data.tgz
Normal file
BIN
openlist/openlist/data.tgz
Normal file
Binary file not shown.
145
openlist/openlist/data/config.json
Normal file
145
openlist/openlist/data/config.json
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"force": false,
|
||||||
|
"site_url": "",
|
||||||
|
"cdn": "",
|
||||||
|
"jwt_secret": "J4l3vzgvqQPWy1yH",
|
||||||
|
"token_expires_in": 48,
|
||||||
|
"database": {
|
||||||
|
"type": "sqlite3",
|
||||||
|
"host": "",
|
||||||
|
"port": 0,
|
||||||
|
"user": "",
|
||||||
|
"password": "",
|
||||||
|
"name": "",
|
||||||
|
"db_file": "data/data.db",
|
||||||
|
"table_prefix": "x_",
|
||||||
|
"ssl_mode": "",
|
||||||
|
"dsn": ""
|
||||||
|
},
|
||||||
|
"meilisearch": {
|
||||||
|
"host": "http://localhost:7700",
|
||||||
|
"api_key": "",
|
||||||
|
"index": "openlist"
|
||||||
|
},
|
||||||
|
"scheme": {
|
||||||
|
"address": "0.0.0.0",
|
||||||
|
"http_port": 5244,
|
||||||
|
"https_port": -1,
|
||||||
|
"force_https": false,
|
||||||
|
"cert_file": "",
|
||||||
|
"key_file": "",
|
||||||
|
"unix_file": "",
|
||||||
|
"unix_file_perm": "",
|
||||||
|
"enable_h2c": false,
|
||||||
|
"enable_h3": false
|
||||||
|
},
|
||||||
|
"temp_dir": "data/temp",
|
||||||
|
"bleve_dir": "data/bleve",
|
||||||
|
"dist_dir": "",
|
||||||
|
"log": {
|
||||||
|
"enable": true,
|
||||||
|
"name": "data/log/log.log",
|
||||||
|
"max_size": 50,
|
||||||
|
"max_backups": 30,
|
||||||
|
"max_age": 28,
|
||||||
|
"compress": false,
|
||||||
|
"filter": {
|
||||||
|
"enable": false,
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"cidr": "",
|
||||||
|
"path": "/ping",
|
||||||
|
"method": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cidr": "",
|
||||||
|
"path": "",
|
||||||
|
"method": "HEAD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cidr": "",
|
||||||
|
"path": "/dav/",
|
||||||
|
"method": "PROPFIND"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delayed_start": 0,
|
||||||
|
"max_buffer_limitMB": -1,
|
||||||
|
"mmap_thresholdMB": 4,
|
||||||
|
"max_connections": 0,
|
||||||
|
"max_concurrency": 64,
|
||||||
|
"tls_insecure_skip_verify": true,
|
||||||
|
"tasks": {
|
||||||
|
"download": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 1,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"transfer": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 2,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 0,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"copy": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 2,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"move": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 2,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"decompress": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 2,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"decompress_upload": {
|
||||||
|
"workers": 5,
|
||||||
|
"max_retry": 2,
|
||||||
|
"task_persistant": false
|
||||||
|
},
|
||||||
|
"allow_retry_canceled": false
|
||||||
|
},
|
||||||
|
"cors": {
|
||||||
|
"allow_origins": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"allow_methods": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"allow_headers": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"s3": {
|
||||||
|
"enable": false,
|
||||||
|
"port": 5246,
|
||||||
|
"ssl": false
|
||||||
|
},
|
||||||
|
"ftp": {
|
||||||
|
"enable": false,
|
||||||
|
"listen": ":5221",
|
||||||
|
"find_pasv_port_attempts": 50,
|
||||||
|
"active_transfer_port_non_20": false,
|
||||||
|
"idle_timeout": 900,
|
||||||
|
"connection_timeout": 30,
|
||||||
|
"disable_active_mode": false,
|
||||||
|
"default_transfer_binary": false,
|
||||||
|
"enable_active_conn_ip_check": true,
|
||||||
|
"enable_pasv_conn_ip_check": true
|
||||||
|
},
|
||||||
|
"sftp": {
|
||||||
|
"enable": false,
|
||||||
|
"listen": ":5222"
|
||||||
|
},
|
||||||
|
"last_launched_version": "refs/heads/main",
|
||||||
|
"proxy_address": ""
|
||||||
|
}
|
||||||
BIN
openlist/openlist/data/data.db-shm
Normal file
BIN
openlist/openlist/data/data.db-shm
Normal file
Binary file not shown.
BIN
openlist/openlist/data/data.db-wal
Normal file
BIN
openlist/openlist/data/data.db-wal
Normal file
Binary file not shown.
BIN
openlist/openlist/linux-amd64.tar.gz
Normal file
BIN
openlist/openlist/linux-amd64.tar.gz
Normal file
Binary file not shown.
BIN
openlist/openlist/linux-arm64.tar.gz
Normal file
BIN
openlist/openlist/linux-arm64.tar.gz
Normal file
Binary file not shown.
12
openlist/openlist/smy-openlist.service
Normal file
12
openlist/openlist/smy-openlist.service
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=openlist
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=/shumengya/bin/openlist
|
||||||
|
ExecStart=/shumengya/bin/openlist/openlist server
|
||||||
|
Restart=on-failure
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
46
openlist/start_openlist.sh
Normal file
46
openlist/start_openlist.sh
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 恢复并启动 openlist 服务
|
||||||
|
# 该脚本用于在执行 stopkill 后恢复服务的正常运行和开机自启
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/openlist/start_openlist.sh" | sudo bash
|
||||||
|
|
||||||
|
SERVICE_NAME="smy-openlist"
|
||||||
|
|
||||||
|
log() { printf '[openlist-启动] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 检查 root 权限
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "正在检查服务状态..."
|
||||||
|
|
||||||
|
# 检查服务文件是否存在
|
||||||
|
if [ ! -f "/etc/systemd/system/${SERVICE_NAME}.service" ]; then
|
||||||
|
fail "未找到服务文件 /etc/systemd/system/${SERVICE_NAME}.service,请先运行安装脚本。"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "正在恢复服务设置..."
|
||||||
|
# 重置可能的失败状态
|
||||||
|
systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
|
||||||
|
log "正在启用并启动服务..."
|
||||||
|
# --now 选项会同时启用(enable)并启动(start)服务
|
||||||
|
systemctl enable --now "$SERVICE_NAME"
|
||||||
|
|
||||||
|
# 等待服务启动
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# 检查启动状态
|
||||||
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
log "服务启动成功!"
|
||||||
|
log "状态: $(systemctl is-active "$SERVICE_NAME")"
|
||||||
|
log "您可以访问 http://IP:5244 使用服务"
|
||||||
|
else
|
||||||
|
log "警告: 服务启动似乎遇到了问题"
|
||||||
|
log "以下是最后 10 行日志:"
|
||||||
|
systemctl status "$SERVICE_NAME" --no-pager -n 10 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
70
openlist/stopkill_openlist.sh
Normal file
70
openlist/stopkill_openlist.sh
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# 强制停止 openlist 服务并清理进程
|
||||||
|
# 用法:curl -fsSL "https://pan.shumengya.top/d/scripts/openlist/stopkill_openlist.sh" | sudo bash
|
||||||
|
|
||||||
|
SERVICE_NAME="smy-openlist"
|
||||||
|
BIN_NAME="openlist"
|
||||||
|
PORT=5244
|
||||||
|
|
||||||
|
log() { printf '[openlist-停止] %s\n' "$*" >&2; }
|
||||||
|
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
log "请使用 root 权限运行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "正在停止 systemd 服务..."
|
||||||
|
# systemctl stop 会将服务状态置为 inactive,systemd 不会自动重启它,除非手动 start/restart
|
||||||
|
# 即使服务被 disable,systemctl restart 仍然可以启动它
|
||||||
|
systemctl disable --now "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
|
||||||
|
log "正在检查残留进程..."
|
||||||
|
# 排除当前脚本自身的 PID,防止误杀
|
||||||
|
current_pid=$$
|
||||||
|
|
||||||
|
# 按进程名强制杀 (使用 -x 精确匹配进程名,避免匹配到脚本自身)
|
||||||
|
pids=$(pgrep -x "$BIN_NAME" || true)
|
||||||
|
# 如果 -x 没找到,尝试 -f 但过滤掉脚本自身
|
||||||
|
if [ -z "$pids" ]; then
|
||||||
|
pids=$(pgrep -f "$BIN_NAME" | grep -v "$current_pid" || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$pids" ]; then
|
||||||
|
log "发现残留进程 PID: $pids,正在强制终止..."
|
||||||
|
kill -9 $pids 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 按端口强制杀
|
||||||
|
pid_port=""
|
||||||
|
if command -v lsof >/dev/null 2>&1; then
|
||||||
|
pid_port=$(lsof -t -i:$PORT 2>/dev/null || true)
|
||||||
|
elif command -v ss >/dev/null 2>&1; then
|
||||||
|
# ss 输出格式处理,提取 pid
|
||||||
|
pid_port=$(ss -lptn "sport = :$PORT" | grep -oE 'pid=[0-9]+' | cut -d= -f2 | sort -u || true)
|
||||||
|
elif command -v netstat >/dev/null 2>&1; then
|
||||||
|
pid_port=$(netstat -nlp | grep ":$PORT " | awk '{print $7}' | cut -d/ -f1 || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$pid_port" ]; then
|
||||||
|
# 再次过滤掉当前 PID (虽然不太可能占用端口,但为了安全)
|
||||||
|
pid_port=$(echo "$pid_port" | grep -v "^$current_pid$")
|
||||||
|
if [ -n "$pid_port" ]; then
|
||||||
|
log "发现端口 $PORT 占用进程 PID: $pid_port,正在强制终止..."
|
||||||
|
kill -9 $pid_port 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 重置服务失败状态,避免 systemd 认为服务处于错误状态
|
||||||
|
systemctl reset-failed "$SERVICE_NAME" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 最终状态检查
|
||||||
|
log "正在验证服务状态..."
|
||||||
|
if pgrep -x "$BIN_NAME" >/dev/null; then
|
||||||
|
log "警告: 进程仍在运行!"
|
||||||
|
ps -fp $(pgrep -x "$BIN_NAME")
|
||||||
|
else
|
||||||
|
log "openlist 已完全停止 (服务已禁用,进程已清理)。"
|
||||||
|
fi
|
||||||
104
openlist/uninstall_openlist.sh
Normal file
104
openlist/uninstall_openlist.sh
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# openlist 一键卸载脚本
|
||||||
|
# 用法:sudo bash uninstall_openlist.sh
|
||||||
|
|
||||||
|
INSTALL_DIR="/shumengya/bin/openlist"
|
||||||
|
SERVICE_NAME="smy-openlist"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
log() { printf '[openlist-卸载] %s\n' "$*" >&2; }
|
||||||
|
fail() { log "错误: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 进度条函数
|
||||||
|
show_progress() {
|
||||||
|
local pid=$1
|
||||||
|
local text=$2
|
||||||
|
local delay=0.1
|
||||||
|
local spin='-\|/'
|
||||||
|
|
||||||
|
printf "[openlist-卸载] %s... " "$text" >&2
|
||||||
|
|
||||||
|
while ps -p "$pid" > /dev/null 2>&1; do
|
||||||
|
local temp=${spin#?}
|
||||||
|
printf "\b%c" "$spin" >&2
|
||||||
|
local spin=$temp${spin%"$temp"}
|
||||||
|
sleep $delay
|
||||||
|
done
|
||||||
|
printf "\b完成\n" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
# 必须使用 root 权限执行
|
||||||
|
if [ "${EUID:-$(id -u)}" -ne 0 ]; then
|
||||||
|
fail "请使用 root 权限运行 (sudo bash uninstall_openlist.sh)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_and_disable_service() {
|
||||||
|
# 停止并禁用 systemd 服务
|
||||||
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
log "正在停止服务 $SERVICE_NAME..."
|
||||||
|
systemctl stop "$SERVICE_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
|
||||||
|
log "正在禁用服务 $SERVICE_NAME..."
|
||||||
|
systemctl disable "$SERVICE_NAME"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_service_file() {
|
||||||
|
# 删除 systemd 服务文件
|
||||||
|
if [ -f "$SERVICE_FILE" ]; then
|
||||||
|
log "正在删除服务文件..."
|
||||||
|
rm -f "$SERVICE_FILE"
|
||||||
|
systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_install_dir() {
|
||||||
|
# 删除安装目录
|
||||||
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
|
log "正在删除安装目录 $INSTALL_DIR..."
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_and_kill_port() {
|
||||||
|
local port=5244
|
||||||
|
if lsof -i :$port >/dev/null 2>&1 || netstat -tunlp | grep -q ":$port "; then
|
||||||
|
log "检测到端口 $port 仍被占用,尝试强制清理..."
|
||||||
|
fuser -k -n tcp $port >/dev/null 2>&1 || true
|
||||||
|
sleep 1
|
||||||
|
# 二次检查
|
||||||
|
local pid
|
||||||
|
pid=$(lsof -t -i:$port 2>/dev/null || true)
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
|
||||||
|
log "开始卸载 openlist..."
|
||||||
|
(stop_and_disable_service) &
|
||||||
|
show_progress $! "正在停止服务"
|
||||||
|
|
||||||
|
(remove_service_file) &
|
||||||
|
show_progress $! "正在删除服务文件"
|
||||||
|
|
||||||
|
check_and_kill_port
|
||||||
|
|
||||||
|
(remove_install_dir) &
|
||||||
|
show_progress $! "正在删除安装目录"
|
||||||
|
|
||||||
|
log "=========================================="
|
||||||
|
log "卸载完成! openlist 已从系统中移除"
|
||||||
|
log "=========================================="
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
201
port-info/AGENTS.md
Normal file
201
port-info/AGENTS.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# AGENTS.md - Agent Coding Guidelines
|
||||||
|
|
||||||
|
This document provides guidelines for agents working in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a **Bash shell script** project for displaying Linux port information (TCP, UDP, HTTP services). The main file is `port_info.sh`.
|
||||||
|
|
||||||
|
## Build / Lint / Test Commands
|
||||||
|
|
||||||
|
### Running the Script
|
||||||
|
```bash
|
||||||
|
# Make executable and run
|
||||||
|
chmod +x port_info.sh
|
||||||
|
./port_info.sh
|
||||||
|
|
||||||
|
# Or run directly with bash
|
||||||
|
bash port_info.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
Use **shellcheck** for static analysis:
|
||||||
|
```bash
|
||||||
|
# Install shellcheck (if not present)
|
||||||
|
apt-get install shellcheck # Debian/Ubuntu
|
||||||
|
yum install epel-release && yum install ShellCheck # RHEL/CentOS
|
||||||
|
|
||||||
|
# Run shellcheck on the script
|
||||||
|
shellcheck port_info.sh
|
||||||
|
|
||||||
|
# Run with specific severity levels
|
||||||
|
shellcheck -S error port_info.sh # Errors only
|
||||||
|
shellcheck -S warning port_info.sh # Warnings and errors
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing a Single Function
|
||||||
|
Bash doesn't have traditional unit tests, but you can test functions interactively:
|
||||||
|
```bash
|
||||||
|
# Source the script and test specific functions
|
||||||
|
source port_info.sh
|
||||||
|
get_service_name 80 # Test port-to-service lookup
|
||||||
|
check_commands # Test command detection
|
||||||
|
```
|
||||||
|
|
||||||
|
### Syntax Validation
|
||||||
|
```bash
|
||||||
|
# Check bash syntax without executing
|
||||||
|
bash -n port_info.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
- Single script files for small utilities
|
||||||
|
- Use clear section separators: `#===============================================================================`
|
||||||
|
- Group related functions together
|
||||||
|
- Main execution at the bottom of the file
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
- Indentation: 4 spaces
|
||||||
|
- Use `#!/bin/bash` shebang (not `#!/bin/sh` for bash-specific features)
|
||||||
|
- Maximum line length: ~100 characters (flexible for output formatting)
|
||||||
|
- Use uppercase for constants/variables, lowercase for function names
|
||||||
|
|
||||||
|
### Variable Naming
|
||||||
|
- Constants (colors, separators): UPPERCASE with underscores, e.g., `RED`, `SEPARATOR`
|
||||||
|
- Global variables: UPPERCASE, e.g., `PORT_CMD`
|
||||||
|
- Local variables in functions: lowercase with underscores, e.g., `local port=$1`
|
||||||
|
- Function names: lowercase with underscores, e.g., `check_commands()`
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
```bash
|
||||||
|
# Good function definition
|
||||||
|
function_name() {
|
||||||
|
local arg1=$1
|
||||||
|
local arg2=$2
|
||||||
|
|
||||||
|
# function body
|
||||||
|
|
||||||
|
return 0 # Always return explicit status
|
||||||
|
}
|
||||||
|
|
||||||
|
# Or with function keyword
|
||||||
|
check_commands() {
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Color Variables
|
||||||
|
Define colors at the top of the script using ANSI escape codes:
|
||||||
|
```bash
|
||||||
|
RED='\033[38;5;196m'
|
||||||
|
GREEN='\033[38;5;46m'
|
||||||
|
NC='\033[0m' # No Color - always reset at end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Use `command -v` to check command availability
|
||||||
|
- Redirect stderr with `2>/dev/null` for expected errors
|
||||||
|
- Exit with status 1 on fatal errors: `exit 1`
|
||||||
|
- Use descriptive error messages with colors
|
||||||
|
|
||||||
|
### Input Handling
|
||||||
|
- Prefer arguments over interactive input
|
||||||
|
- Validate inputs before processing
|
||||||
|
- Use `local` for all function parameters
|
||||||
|
- Check for required arguments
|
||||||
|
|
||||||
|
### String Comparisons
|
||||||
|
```bash
|
||||||
|
# String equality
|
||||||
|
if [ "$var" = "value" ]; then
|
||||||
|
# ...
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Numeric comparison
|
||||||
|
if [ "$num" -eq 0 ]; then
|
||||||
|
# ...
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Regex matching
|
||||||
|
if [[ "$port" =~ ^[0-9]+$ ]]; then
|
||||||
|
# ...
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output Formatting
|
||||||
|
- Use `printf` for formatted output (not `echo` for complex output)
|
||||||
|
- Use color codes with `${COLOR}...${NC}` pattern
|
||||||
|
- Consistent column widths in tables
|
||||||
|
|
||||||
|
### Common Patterns
|
||||||
|
|
||||||
|
#### Command Availability Check
|
||||||
|
```bash
|
||||||
|
if command -v ss &> /dev/null; then
|
||||||
|
PORT_CMD="ss"
|
||||||
|
elif command -v netstat &> /dev/null; then
|
||||||
|
PORT_CMD="netstat"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: command not found${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reading Lines
|
||||||
|
```bash
|
||||||
|
while IFS= read -r line; do
|
||||||
|
# process line
|
||||||
|
done < <(command)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Safe Variable Usage
|
||||||
|
- Always quote variables: `"$var"` not `$var`
|
||||||
|
- Use `${var}` for clarity in complex expressions
|
||||||
|
- Initialize variables before use
|
||||||
|
|
||||||
|
### Shellcheck Compliance
|
||||||
|
Run `shellcheck` and fix all warnings:
|
||||||
|
- SC2086: Quote variables to prevent word splitting
|
||||||
|
- SC2166: Use `&&` instead of `-a` in test expressions
|
||||||
|
- SC2027: Quote strings containing newlines
|
||||||
|
- SC2248: Prefer printf over echo
|
||||||
|
|
||||||
|
## File Extensions
|
||||||
|
- Bash scripts: `.sh`
|
||||||
|
- No extension for executable scripts (optional)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- Add Chinese comments for Chinese-language projects
|
||||||
|
- Use section headers: `#===============================================================================`
|
||||||
|
- Document function purpose before definition
|
||||||
|
- Include usage information in comments
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
This script requires:
|
||||||
|
- `ss` or `netstat net` (from-tools package)
|
||||||
|
- Standard Linux utilities: `awk`, `grep`, `sed`, `sort`, `uniq`
|
||||||
|
|
||||||
|
## Common Tasks
|
||||||
|
|
||||||
|
### Adding a New Port Service
|
||||||
|
Edit `get_service_name()` function in `port_info.sh`:
|
||||||
|
```bash
|
||||||
|
get_service_name() {
|
||||||
|
local port=$1
|
||||||
|
case $port in
|
||||||
|
80) echo "HTTP" ;;
|
||||||
|
443) echo "HTTPS" ;;
|
||||||
|
3306) echo "MySQL" ;;
|
||||||
|
# Add new port here
|
||||||
|
9000) echo "PHP-FPM" ;;
|
||||||
|
*) echo "未知" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a New Display Section
|
||||||
|
1. Create a new function following existing patterns
|
||||||
|
2. Add function call in `main()`
|
||||||
|
3. Use consistent color scheme and formatting
|
||||||
260
port-info/port_info.sh
Executable file
260
port-info/port_info.sh
Executable file
@@ -0,0 +1,260 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
# Linux 端口信息查看脚本
|
||||||
|
# 兼容: Ubuntu / Debian 及衍生版
|
||||||
|
# 功能: 显示 TCP、UDP、HTTP 端口服务信息
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
##
|
||||||
|
RED='\033[38;5;196m'
|
||||||
|
GREEN='\033[38;5;46m'
|
||||||
|
YELLOW='\033[38;5;226m'
|
||||||
|
BLUE='\033[38;5;21m'
|
||||||
|
MAGENTA='\033[38;5;201m'
|
||||||
|
CYAN='\033[38;5;51m'
|
||||||
|
WHITE='\033[38;5;255m'
|
||||||
|
ORANGE='\033[38;5;208m'
|
||||||
|
PINK='\033[38;5;219m'
|
||||||
|
LIME='\033[38;5;154m'
|
||||||
|
PURPLE='\033[38;5;141m'
|
||||||
|
GOLD='\033[38;5;220m'
|
||||||
|
GRAY='\033[38;5;244m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SEPARATOR="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
check_commands() {
|
||||||
|
# echo -e "${CYAN}▸ 检测命令...${NC}"
|
||||||
|
|
||||||
|
if command -v ss &> /dev/null; then
|
||||||
|
PORT_CMD="ss"
|
||||||
|
elif command -v netstat &> /dev/null; then
|
||||||
|
PORT_CMD="netstat"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ 错误: 未找到 ss 或 netstat 命令${NC}"
|
||||||
|
echo -e "${YELLOW} 请安装: apt-get install net-tools${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# echo -e "${GREEN}✓ 使用命令: ${PORT_CMD}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
##
|
||||||
|
get_service_name() {
|
||||||
|
local port=$1
|
||||||
|
case $port in
|
||||||
|
20) echo "FTP-DATA" ;;
|
||||||
|
21) echo "FTP" ;;
|
||||||
|
22) echo "SSH" ;;
|
||||||
|
23) echo "Telnet" ;;
|
||||||
|
25) echo "SMTP" ;;
|
||||||
|
53) echo "DNS" ;;
|
||||||
|
67|68) echo "DHCP" ;;
|
||||||
|
80) echo "HTTP" ;;
|
||||||
|
110) echo "POP3" ;;
|
||||||
|
123) echo "NTP" ;;
|
||||||
|
443) echo "HTTPS" ;;
|
||||||
|
3306) echo "MySQL" ;;
|
||||||
|
3389) echo "RDP" ;;
|
||||||
|
5432) echo "PostgreSQL" ;;
|
||||||
|
6379) echo "Redis" ;;
|
||||||
|
8080) echo "HTTP-Proxy" ;;
|
||||||
|
8443) echo "HTTPS-Alt" ;;
|
||||||
|
27017) echo "MongoDB" ;;
|
||||||
|
*) echo "未知" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
show_tcp_ports() {
|
||||||
|
echo -e "\n${SEPARATOR}"
|
||||||
|
echo -e "${BLUE} 🔵 TCP 端口监听列表 ${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
printf "${GREEN}%-6s %-10s %-14s %-10s %-20s${NC}\n" "协议" "端口" "服务(参考)" "PID" "进程名称"
|
||||||
|
echo -e "${GRAY}──────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
if [ "$PORT_CMD" = "ss" ]; then
|
||||||
|
ss -tulnp 2>/dev/null | grep LISTEN | while IFS= read -r line; do
|
||||||
|
addr=$(echo "$line" | awk '{print $5}')
|
||||||
|
port="${addr##*:}"
|
||||||
|
[[ "$port" =~ ^[0-9]+$ ]] || continue
|
||||||
|
|
||||||
|
prog_field=$(echo "$line" | awk '{print $NF}')
|
||||||
|
pid=$(echo "$prog_field" | grep -oP 'pid=\K[0-9]+' | head -1)
|
||||||
|
name=$(echo "$prog_field" | sed -n 's/.*("\([^"]*\)".*/\1/p' | head -1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
printf "TCP|%-10s|%-14s|%-10s|%-20s\n" "$port" "$service" "$pid" "$name"
|
||||||
|
done | sort -t'|' -k2 -n | uniq | while IFS='|' read -r p s rv pi n; do
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-20s${NC}\n" "$p" "$s" "$rv" "$pi" "$n"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
netstat -tulnp 2>/dev/null | grep LISTEN | while read -r line; do
|
||||||
|
port=$(echo "$line" | awk '{split($4,a,":"); print a[length(a)]}')
|
||||||
|
[[ "$port" =~ ^[0-9]+$ ]] || continue
|
||||||
|
|
||||||
|
prog=$(echo "$line" | awk '{print $NF}')
|
||||||
|
pid=$(echo "$prog" | cut -d'/' -f1)
|
||||||
|
name=$(echo "$prog" | cut -d'/' -f2 | cut -d' ' -f1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
printf "TCP|%-10s|%-14s|%-10s|%-20s\n" "$port" "$service" "$pid" "$name"
|
||||||
|
done | sort -t'|' -k2 -n | uniq | while IFS='|' read -r p s rv pi n; do
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-20s${NC}\n" "TCP" "$p" "$s" "$pi" "$n"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GRAY}──────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
show_udp_ports() {
|
||||||
|
echo -e "\n${SEPARATOR}"
|
||||||
|
echo -e "${MAGENTA} 🟣 UDP 端口监听列表 ${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
printf "${PINK}%-6s %-10s %-14s %-10s %-20s${NC}\n" "协议" "端口" "服务(参考)" "PID" "进程名称"
|
||||||
|
echo -e "${GRAY}──────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
if [ "$PORT_CMD" = "ss" ]; then
|
||||||
|
ss -ulnp 2>/dev/null | awk 'NR>1 && $1!="State"' | while IFS= read -r line; do
|
||||||
|
addr=$(echo "$line" | awk '{print $4}')
|
||||||
|
port="${addr##*:}"
|
||||||
|
[[ "$port" =~ ^[0-9]+$ ]] || continue
|
||||||
|
|
||||||
|
prog_field=$(echo "$line" | awk '{print $NF}')
|
||||||
|
pid=$(echo "$prog_field" | grep -oP 'pid=\K[0-9]+' | head -1)
|
||||||
|
name=$(echo "$prog_field" | sed -n 's/.*("\([^"]*\)".*/\1/p' | head -1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
printf "UDP|%-10s|%-14s|%-10s|%-20s\n" "$port" "$service" "$pid" "$name"
|
||||||
|
done | sort -t'|' -k2 -n | uniq | while IFS='|' read -r p s rv pi n; do
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-20s${NC}\n" "$p" "$s" "$rv" "$pi" "$n"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
netstat -ulnp 2>/dev/null | awk 'NR>1' | while read -r line; do
|
||||||
|
port=$(echo "$line" | awk '{split($4,a,":"); print a[length(a)]}')
|
||||||
|
[[ "$port" =~ ^[0-9]+$ ]] || continue
|
||||||
|
|
||||||
|
prog=$(echo "$line" | awk '{print $NF}')
|
||||||
|
pid=$(echo "$prog" | cut -d'/' -f1)
|
||||||
|
name=$(echo "$prog" | cut -d'/' -f2 | cut -d' ' -f1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
printf "UDP|%-10s|%-14s|%-10s|%-20s\n" "$port" "$service" "$pid" "$name"
|
||||||
|
done | sort -t'|' -k2 -n | uniq | while IFS='|' read -r p s rv pi n; do
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-20s${NC}\n" "$p" "$s" "$rv" "$pi" "$n"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GRAY}──────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
show_http_ports() {
|
||||||
|
echo -e "\n${SEPARATOR}"
|
||||||
|
echo -e "${YELLOW} 🟡 HTTP/HTTPS 常用端口 ${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
printf "${ORANGE}%-6s %-10s %-14s %-10s %-10s %-20s${NC}\n" "协议" "端口" "服务(参考)" "状态" "PID" "进程名称"
|
||||||
|
echo -e "${GRAY}────────────────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
|
||||||
|
http_ports="80 443 8080 8443 8000 8888 3000 5000"
|
||||||
|
|
||||||
|
for port in $http_ports; do
|
||||||
|
if [ "$PORT_CMD" = "ss" ]; then
|
||||||
|
result=$(ss -tulnp 2>/dev/null | grep ":$port " | grep -v grep)
|
||||||
|
if [ -n "$result" ]; then
|
||||||
|
proto=$(echo "$result" | awk '{print $1}' | head -1 | tr '[:upper:]' '[:lower:]')
|
||||||
|
prog_field=$(echo "$result" | awk '{print $NF}')
|
||||||
|
pid=$(echo "$prog_field" | grep -oP 'pid=\K[0-9]+' | head -1)
|
||||||
|
name=$(echo "$prog_field" | sed -n 's/.*("\([^"]*\)".*/\1/p' | head -1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-10s${NC} %-20s${NC}\n" "$proto" "$port" "$service" "监听中" "$pid" "$name"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
result=$(netstat -tulnp 2>/dev/null | grep ":$port " | grep -v grep)
|
||||||
|
if [ -n "$result" ]; then
|
||||||
|
proto=$(echo "$result" | awk '{print $6}' | head -1 | tr '[:upper:]' '[:lower:]')
|
||||||
|
prog=$(echo "$result" | awk '{print $7}')
|
||||||
|
pid=$(echo "$prog" | cut -d'/' -f1)
|
||||||
|
name=$(echo "$prog" | cut -d'/' -f2 | cut -d' ' -f1)
|
||||||
|
|
||||||
|
[ -z "$pid" ] && pid="-"
|
||||||
|
[ -z "$name" ] && name="-"
|
||||||
|
|
||||||
|
service=$(get_service_name "$port")
|
||||||
|
|
||||||
|
printf "${LIME}%-6s${NC} %-10s${NC} %-14s${NC} %-10s${NC} %-10s${NC} %-20s${NC}\n" "$proto" "$port" "$service" "监听中" "$pid" "$name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${GRAY}────────────────────────────────────────────────────────────────────────────────────────${NC}"
|
||||||
|
echo -e "${GRAY}※ 服务(参考) 列为常用端口的默认服务名称,仅供参考,实际占用程序以进程名称列为准${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
show_statistics() {
|
||||||
|
echo -e "\n${SEPARATOR}"
|
||||||
|
echo -e "${CYAN} 📊 端口统计信息 ${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
|
||||||
|
if [ "$PORT_CMD" = "ss" ]; then
|
||||||
|
tcp_count=$(ss -tuln 2>/dev/null | grep -c LISTEN)
|
||||||
|
udp_count=$(ss -uln 2>/dev/null | awk 'NR>1 && $1 != "State" && /:[0-9]+/ {print}' | wc -l)
|
||||||
|
else
|
||||||
|
tcp_count=$(netstat -tuln 2>/dev/null | grep -c LISTEN)
|
||||||
|
udp_count=$(netstat -uln 2>/dev/null | awk 'NR>1 && /:[0-9]+/ {print}' | wc -l)
|
||||||
|
fi
|
||||||
|
|
||||||
|
total=$((tcp_count + udp_count))
|
||||||
|
|
||||||
|
printf " ${BLUE}TCP 监听端口:${NC} ${GREEN}%d${NC}\n" "$tcp_count"
|
||||||
|
printf " ${MAGENTA}UDP 监听端口:${NC} ${GREEN}%d${NC}\n" "$udp_count"
|
||||||
|
printf " ${YELLOW}总 计:${NC} ${ORANGE}%d${NC}\n" "$total"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
show_header() {
|
||||||
|
clear
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
echo -e "${PURPLE} ║ Linux 系统端口信息查看工具${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
echo -e " ${GOLD}▸ 兼容系统:${NC} ${WHITE}Ubuntu / Debian 及衍生版本${NC}"
|
||||||
|
echo -e " ${GOLD}▸ 生成时间:${NC} ${WHITE}$(date '+%Y-%m-%d %H:%M:%S')${NC}"
|
||||||
|
echo -e "${SEPARATOR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
main() {
|
||||||
|
show_header
|
||||||
|
check_commands
|
||||||
|
show_tcp_ports
|
||||||
|
show_udp_ports
|
||||||
|
show_http_ports
|
||||||
|
show_statistics
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
48
ssh/alycd.sh
Normal file
48
ssh/alycd.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="47.108.90.0" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
48
ssh/alyxg.sh
Normal file
48
ssh/alyxg.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="47.76.191.104" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
48
ssh/bigmengya.sh
Normal file
48
ssh/bigmengya.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="10.0.0.233" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
48
ssh/mengyathree.sh
Normal file
48
ssh/mengyathree.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="10.0.0.33" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
48
ssh/redmi.sh
Normal file
48
ssh/redmi.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="10.0.0.10" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
48
ssh/smallmengya.sh
Normal file
48
ssh/smallmengya.sh
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ssh_connect_embedded.sh
|
||||||
|
# 在脚本内直接写明密码(仅在你确认在安全内网且风险可接受时使用)
|
||||||
|
# Usage: ./bigmengya.sh [user] [host] [port]
|
||||||
|
# Example: ./bigmengya.sh alice 10.0.0.5 22
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# << 在这里修改密码等配置 >>
|
||||||
|
# -------------------------
|
||||||
|
USER="root" # 可通过第一个参数覆盖
|
||||||
|
HOST="10.0.0.100" # 可通过第二个参数覆盖
|
||||||
|
PORT="22" # 可通过第三个参数覆盖
|
||||||
|
PASSWORD="tyh@19900420"
|
||||||
|
# -------------------------
|
||||||
|
# 结束配置
|
||||||
|
# -------------------------
|
||||||
|
|
||||||
|
# 小安全缓解措施
|
||||||
|
umask 077 # 新创建文件仅对当前用户可读写
|
||||||
|
trap 'unset PASSWORD' EXIT INT TERM # 退出时尝试清除变量
|
||||||
|
|
||||||
|
# 可选:如果想在调用时强制使用脚本内的 USER/HOST 而不允许参数覆盖,
|
||||||
|
# 请把上面 USER/HOST 赋值方式改为固定字面量。
|
||||||
|
|
||||||
|
# 检查 sshpass 是否存在
|
||||||
|
if ! command -v sshpass >/dev/null 2>&1; then
|
||||||
|
echo "错误:找不到 sshpass。请安装后重试。例如 Debian/Ubuntu: sudo apt install sshpass"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 连接(禁用第一次主机密钥交互以便脚本自动连接)
|
||||||
|
# 如果你希望每次确认主机密钥,可以去掉 -o StrictHostKeyChecking=no
|
||||||
|
# 添加以下选项:
|
||||||
|
# -o PreferredAuthentications=password 强制使用密码认证
|
||||||
|
# -o PubkeyAuthentication=no 禁用公钥认证
|
||||||
|
# -o PasswordAuthentication=yes 启用密码认证
|
||||||
|
# -t 强制分配伪终端(解决curl|bash问题)
|
||||||
|
sshpass -p "$PASSWORD" ssh -t \
|
||||||
|
-o StrictHostKeyChecking=no \
|
||||||
|
-o PreferredAuthentications=password \
|
||||||
|
-o PubkeyAuthentication=no \
|
||||||
|
-o PasswordAuthentication=yes \
|
||||||
|
-p "$PORT" "$USER@$HOST"
|
||||||
|
|
||||||
|
# 在脚本尾尝试清理(再次)
|
||||||
|
unset PASSWORD
|
||||||
67
systemctl-info/AGENTS.md
Normal file
67
systemctl-info/AGENTS.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# AGENTS.md - iFlow CLI 项目指南
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
这是一个 **systemctl 详细信息查看脚本** (v2.0 模块化版本),用于在 Linux 系统中显示 Systemd 相关的全面信息。
|
||||||
|
|
||||||
|
### 项目类型
|
||||||
|
- **语言**: Bash Shell 脚本
|
||||||
|
- **平台**: Linux
|
||||||
|
- **版本**: 2.0 (模块化版本)
|
||||||
|
- **用途**: 系统管理、信息查看
|
||||||
|
|
||||||
|
### 模块化结构
|
||||||
|
|
||||||
|
脚本已重构为 22 个独立的功能模块,每个模块对应一个 `module_xxx()` 函数:
|
||||||
|
|
||||||
|
| 模块编号 | 模块名称 | 功能描述 |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| 1 | `module_systemd_version` | Systemd 版本信息 |
|
||||||
|
| 2 | `module_system_info` | 系统基础信息 (主机名、内核、OS等) |
|
||||||
|
| 3 | `module_systemd_status` | Systemd 系统状态 |
|
||||||
|
| 4 | `module_service_stats` | 服务统计信息 |
|
||||||
|
| 5 | `module_failed_services` | 失败的服务详情 |
|
||||||
|
| 6 | `module_masked_services` | 已屏蔽(Masked)的服务 |
|
||||||
|
| 7 | `module_running_services` | 运行中的服务列表 |
|
||||||
|
| 8 | `module_timer` | Timer 定时任务 |
|
||||||
|
| 9 | `module_socket` | Socket 监听单元 |
|
||||||
|
| 10 | `module_target` | Target 目标单元 |
|
||||||
|
| 11 | `module_mount` | Mount 和 Automount 挂载点 |
|
||||||
|
| 12 | `module_path` | Path 路径监控单元 (新增) |
|
||||||
|
| 13 | `module_device` | Device 设备单元 (新增) |
|
||||||
|
| 14 | `module_scope_slice` | Scope 和 Slice 资源控制单元 (新增) |
|
||||||
|
| 15 | `module_dependencies` | 系统依赖关系 (新增) |
|
||||||
|
| 16 | `module_journal` | Journal 日志摘要 |
|
||||||
|
| 17 | `module_environment` | Systemd 环境变量 |
|
||||||
|
| 18 | `module_cgroup` | Cgroup 信息 (新增) |
|
||||||
|
| 19 | `module_performance` | 系统性能信息 |
|
||||||
|
| 20 | `module_power_management` | 电源管理状态 (新增) |
|
||||||
|
| 21 | `module_critical_services` | 关键系统服务状态 |
|
||||||
|
| 22 | `module_help` | 常用命令提示 |
|
||||||
|
|
||||||
|
### 新增功能 (相比 v1.0)
|
||||||
|
|
||||||
|
- **Path 单元**: 路径监控单元的状态和监控路径
|
||||||
|
- **Device 单元**: 设备单元的详细信息
|
||||||
|
- **Scope 和 Slice**: 资源控制单元的统计和资源使用
|
||||||
|
- **依赖关系**: 系统单元依赖树和依赖数量统计
|
||||||
|
- **Cgroup 信息**: cgroup 版本、控制器、资源使用
|
||||||
|
- **电源管理**: suspend/hibernate 状态和日志
|
||||||
|
- **已屏蔽服务**: 单独列出所有被屏蔽的服务
|
||||||
|
|
||||||
|
## 运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 直接运行脚本
|
||||||
|
./systemctl-info
|
||||||
|
|
||||||
|
# 或使用 bash 执行
|
||||||
|
bash systemctl-info
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 需要 root 权限或适当的 systemd 访问权限
|
||||||
|
- 依赖系统命令:`systemctl`, `journalctl`, `uptime`, `free`, `top`, `hostname`, `uname`, `numfmt`
|
||||||
|
- 脚本会使用 `journalctl` 查看错误日志,可能需要相应权限
|
||||||
|
- 脚本输出使用 ANSI 颜色代码,建议在支持颜色的终端中运行
|
||||||
802
systemctl-info/systemctl-info
Executable file
802
systemctl-info/systemctl-info
Executable file
@@ -0,0 +1,802 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# systemctl-info - 详细的systemctl信息查看脚本
|
||||||
|
# 作者: iFlow CLI
|
||||||
|
# 日期: 2026-02-13
|
||||||
|
# 版本: 2.0 (模块化版本)
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 颜色定义
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
MAGENTA='\033[0;35m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
WHITE='\033[0;37m'
|
||||||
|
BRIGHT_RED='\033[1;31m'
|
||||||
|
BRIGHT_GREEN='\033[1;32m'
|
||||||
|
BRIGHT_YELLOW='\033[1;33m'
|
||||||
|
BRIGHT_BLUE='\033[1;34m'
|
||||||
|
BRIGHT_MAGENTA='\033[1;35m'
|
||||||
|
BRIGHT_CYAN='\033[1;36m'
|
||||||
|
BRIGHT_WHITE='\033[1;37m'
|
||||||
|
ORANGE='\033[38;5;208m'
|
||||||
|
PINK='\033[38;5;205m'
|
||||||
|
PURPLE='\033[38;5;141m'
|
||||||
|
LIME='\033[38;5;154m'
|
||||||
|
RESET='\033[0m'
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 分割线样式
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
SEPARATOR="${BRIGHT_CYAN}═══════════════════════════════════════════════════════════════════════════════${RESET}"
|
||||||
|
THIN_SEPARATOR="${CYAN}──────────────────────────────────────────────────────────────────────────────────────${RESET}"
|
||||||
|
DASH_SEPARATOR="${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
|
||||||
|
LINE="${BRIGHT_CYAN}═══════════════════════════════════════════════════════════════════════════════${RESET}"
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 通用工具函数
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# 打印标题
|
||||||
|
print_header() {
|
||||||
|
echo -e "${BRIGHT_CYAN}[Systemctl Info Viewer v2.0]${RESET}"
|
||||||
|
echo -e "${BRIGHT_CYAN}模块化 Systemd 信息查看脚本${RESET}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印带颜色的小节标题
|
||||||
|
print_section() {
|
||||||
|
echo -e "${THIN_SEPARATOR}"
|
||||||
|
echo -e "${BRIGHT_BLUE}[ $1 ]${RESET}"
|
||||||
|
echo -e "${THIN_SEPARATOR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印带图标的信息行
|
||||||
|
print_info() {
|
||||||
|
local icon="$1"
|
||||||
|
local label="$2"
|
||||||
|
local value="$3"
|
||||||
|
local color="$4"
|
||||||
|
echo -e "${icon} ${BRIGHT_WHITE}${label}:${RESET} ${color}${value}${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印子项
|
||||||
|
print_subitem() {
|
||||||
|
local label="$1"
|
||||||
|
local value="$2"
|
||||||
|
local color="$3"
|
||||||
|
echo -e " ${BRIGHT_CYAN}▸${RESET} ${BRIGHT_WHITE}${label}:${RESET} ${color}${value}${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印带颜色的列表项
|
||||||
|
print_list_item() {
|
||||||
|
local icon="$1"
|
||||||
|
local name="$2"
|
||||||
|
local status="$3"
|
||||||
|
local status_color="$4"
|
||||||
|
local extra="$5"
|
||||||
|
printf "${icon} ${BRIGHT_WHITE}%-45s${RESET} ${status_color}%s${RESET}%s\n" "$name" "$status" "$extra"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取状态图标和颜色
|
||||||
|
get_state_icon_color() {
|
||||||
|
local state="$1"
|
||||||
|
case "$state" in
|
||||||
|
active|running|listening)
|
||||||
|
echo -e "✅|${BRIGHT_GREEN}"
|
||||||
|
;;
|
||||||
|
inactive)
|
||||||
|
echo -e "⭕|${BRIGHT_YELLOW}"
|
||||||
|
;;
|
||||||
|
failed)
|
||||||
|
echo -e "❌|${BRIGHT_RED}"
|
||||||
|
;;
|
||||||
|
activating|deactivating)
|
||||||
|
echo -e "🔄|${BRIGHT_CYAN}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "❓|${BRIGHT_WHITE}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块1: Systemd 版本信息
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_systemd_version() {
|
||||||
|
print_section "📋 Systemd 版本信息"
|
||||||
|
|
||||||
|
SYSTEMD_VERSION=$(systemctl --version | head -n 1 | awk '{print $2}')
|
||||||
|
FEATURE_COUNT=$(systemctl --version | grep -c "features")
|
||||||
|
|
||||||
|
print_info "🔧" "Systemd 版本" "$SYSTEMD_VERSION" "${BRIGHT_GREEN}"
|
||||||
|
print_info "✨" "支持功能特性" "$FEATURE_COUNT 项" "${LIME}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}详细版本信息:${RESET}"
|
||||||
|
systemctl --version | while IFS= read -r line; do
|
||||||
|
echo -e " ${CYAN}${line}${RESET}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块2: 系统基础信息
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_system_info() {
|
||||||
|
print_section "🖥️ 系统基础信息"
|
||||||
|
|
||||||
|
HOSTNAME=$(hostname)
|
||||||
|
KERNEL_VERSION=$(uname -r)
|
||||||
|
OS_ID=$(grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
||||||
|
OS_NAME=$(grep '^PRETTY_NAME=' /etc/os-release | cut -d'=' -f2 | tr -d '"')
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
UPTIME=$(uptime -p)
|
||||||
|
|
||||||
|
print_info "🖥️" "主机名" "$HOSTNAME" "${BRIGHT_YELLOW}"
|
||||||
|
print_info "🐧" "内核版本" "$KERNEL_VERSION" "${ORANGE}"
|
||||||
|
print_info "📦" "操作系统ID" "$OS_ID" "${PINK}"
|
||||||
|
print_info "💻" "系统名称" "$OS_NAME" "${PURPLE}"
|
||||||
|
print_info "🏗️" "系统架构" "$ARCH" "${BRIGHT_CYAN}"
|
||||||
|
print_info "⏱️" "系统运行时间" "$UPTIME" "${LIME}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块3: Systemd 系统状态
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_systemd_status() {
|
||||||
|
print_section "⚙️ Systemd 系统状态"
|
||||||
|
|
||||||
|
SYSTEM_STATE=$(systemctl is-system-running)
|
||||||
|
DEFAULT_TARGET=$(systemctl get-default)
|
||||||
|
INIT_PID=$(systemctl show --property=MainPID --value)
|
||||||
|
BOOT_TIME=$(systemctl show --property=UserspaceTimestamp --value | cut -d' ' -f1)
|
||||||
|
|
||||||
|
case $SYSTEM_STATE in
|
||||||
|
running)
|
||||||
|
STATE_COLOR="${BRIGHT_GREEN}"
|
||||||
|
STATE_ICON="✅"
|
||||||
|
;;
|
||||||
|
degraded)
|
||||||
|
STATE_COLOR="${BRIGHT_YELLOW}"
|
||||||
|
STATE_ICON="⚠️"
|
||||||
|
;;
|
||||||
|
maintenance)
|
||||||
|
STATE_COLOR="${BRIGHT_RED}"
|
||||||
|
STATE_ICON="🔧"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
STATE_COLOR="${BRIGHT_WHITE}"
|
||||||
|
STATE_ICON="❓"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
print_info "$STATE_ICON" "系统状态" "$SYSTEM_STATE" "$STATE_COLOR"
|
||||||
|
print_info "🎯" "默认运行级别" "$DEFAULT_TARGET" "${BRIGHT_CYAN}"
|
||||||
|
print_info "🔄" "Init 进程 PID" "$INIT_PID" "${ORANGE}"
|
||||||
|
print_info "🚀" "启动时间" "$BOOT_TIME" "${PURPLE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块4: 服务(Service)统计与状态
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_service_stats() {
|
||||||
|
print_section "🔌 服务(Service)统计与状态"
|
||||||
|
|
||||||
|
TOTAL_UNITS=$(systemctl list-unit-files --type=service --no-legend | wc -l)
|
||||||
|
ENABLED_SERVICES=$(systemctl list-unit-files --type=service --state=enabled --no-legend | wc -l)
|
||||||
|
DISABLED_SERVICES=$(systemctl list-unit-files --type=service --state=disabled --no-legend | wc -l)
|
||||||
|
STATIC_SERVICES=$(systemctl list-unit-files --type=service --state=static --no-legend | wc -l)
|
||||||
|
MASKED_SERVICES=$(systemctl list-unit-files --type=service --state=masked --no-legend | wc -l)
|
||||||
|
|
||||||
|
RUNNING_SERVICES=$(systemctl list-units --type=service --state=running --no-legend | wc -l)
|
||||||
|
FAILED_SERVICES=$(systemctl list-units --type=service --state=failed --no-legend | wc -l)
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}服务文件统计:${RESET}"
|
||||||
|
print_subitem "总服务数" "$TOTAL_UNITS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "已启用(enabled)" "$ENABLED_SERVICES" "${BRIGHT_GREEN}"
|
||||||
|
print_subitem "已禁用(disabled)" "$DISABLED_SERVICES" "${BRIGHT_RED}"
|
||||||
|
print_subitem "静态服务(static)" "$STATIC_SERVICES" "${BRIGHT_YELLOW}"
|
||||||
|
print_subitem "已屏蔽(masked)" "$MASKED_SERVICES" "${PURPLE}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}服务运行状态:${RESET}"
|
||||||
|
print_subitem "运行中" "$RUNNING_SERVICES" "${LIME}"
|
||||||
|
print_subitem "失败" "$FAILED_SERVICES" "${BRIGHT_RED}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块5: 失败的服务详情
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_failed_services() {
|
||||||
|
FAILED_SERVICES=$(systemctl list-units --type=service --state=failed --no-legend | wc -l)
|
||||||
|
|
||||||
|
if [ "$FAILED_SERVICES" -gt 0 ]; then
|
||||||
|
print_section "❌ 失败的服务详情 (共 $FAILED_SERVICES 个)"
|
||||||
|
systemctl list-units --type=service --state=failed --no-pager | sed '1,1d' | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
SERVICE_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
LOAD_STATE=$(echo "$line" | awk '{print $2}')
|
||||||
|
ACTIVE_STATE=$(echo "$line" | awk '{print $3}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
DESCRIPTION=$(echo "$line" | awk '{for(i=5;i<=NF;i++)print $i}' | tr '\n' ' ' | sed 's/ $//')
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_RED}✗${RESET} ${BRIGHT_WHITE}${SERVICE_NAME}${RESET}"
|
||||||
|
echo -e " ${CYAN}描述:${RESET} ${WHITE}${DESCRIPTION}${RESET}"
|
||||||
|
echo -e " ${CYAN}状态:${RESET} ${RED}${LOAD_STATE}${RESET}|${RED}${ACTIVE_STATE}${RESET}|${RED}${SUB_STATE}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
print_section "✅ 失败的服务详情"
|
||||||
|
echo -e "${BRIGHT_GREEN} 没有失败的服务${RESET}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块6: 已屏蔽(Masked)的服务
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_masked_services() {
|
||||||
|
MASKED_COUNT=$(systemctl list-unit-files --type=service --state=masked --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_section "🚫 已屏蔽(Masked)的服务"
|
||||||
|
print_subitem "已屏蔽服务数" "$MASKED_COUNT" "${PURPLE}"
|
||||||
|
|
||||||
|
if [ "$MASKED_COUNT" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}已屏蔽的服务列表:${RESET}"
|
||||||
|
systemctl list-unit-files --type=service --state=masked --no-legend | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
echo -e " ${PURPLE}✗${RESET} ${BRIGHT_WHITE}${line}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块7: 运行中的服务
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_running_services() {
|
||||||
|
print_section "🟢 运行中的服务 (Top 20)"
|
||||||
|
|
||||||
|
RUNNING_COUNT=$(systemctl list-units --type=service --state=running --no-legend | wc -l)
|
||||||
|
print_subitem "运行中服务总数" "$RUNNING_COUNT" "${LIME}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}运行中的服务列表:${RESET}"
|
||||||
|
systemctl list-units --type=service --state=running --no-pager --no-legend | head -20 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
SERVICE_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
LOAD_STATE=$(echo "$line" | awk '{print $2}')
|
||||||
|
ACTIVE_STATE=$(echo "$line" | awk '{print $3}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
|
||||||
|
printf " ${BRIGHT_GREEN}●${RESET} ${BRIGHT_WHITE}%-40s${RESET} ${CYAN}%-8s${RESET} ${BRIGHT_GREEN}%-10s${RESET} ${BRIGHT_CYAN}%s${RESET}\n" "$SERVICE_NAME" "$LOAD_STATE" "$ACTIVE_STATE" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块8: Timer 定时任务
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_timer() {
|
||||||
|
print_section "⏰ Timer 定时任务"
|
||||||
|
|
||||||
|
TOTAL_TIMERS=$(systemctl list-units --type=timer --all --no-legend | wc -l)
|
||||||
|
ACTIVE_TIMERS=$(systemctl list-units --type=timer --state=active --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "总 Timer 数" "$TOTAL_TIMERS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃 Timer" "$ACTIVE_TIMERS" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_TIMERS" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}活跃的定时任务 (Top 15):${RESET}"
|
||||||
|
systemctl list-units --type=timer --state=active --no-pager --no-legend | head -15 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
TIMER_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
NEXT_RUN=$(systemctl show "$TIMER_NAME" --property=NextElapseUSec --value 2>/dev/null)
|
||||||
|
|
||||||
|
printf " ${BRIGHT_YELLOW}⏱${RESET} ${BRIGHT_WHITE}%-40s${RESET} ${CYAN}下次执行:${RESET} ${LIME}%s${RESET}\n" "$TIMER_NAME" "$NEXT_RUN"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块9: Socket 单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_socket() {
|
||||||
|
print_section "🔌 Socket 监听"
|
||||||
|
|
||||||
|
TOTAL_SOCKETS=$(systemctl list-units --type=socket --all --no-legend | wc -l)
|
||||||
|
LISTENING_SOCKETS=$(systemctl list-units --type=socket --state=listening --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "总 Socket 数" "$TOTAL_SOCKETS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "监听中" "$LISTENING_SOCKETS" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$LISTENING_SOCKETS" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}正在监听的 Socket (Top 15):${RESET}"
|
||||||
|
systemctl list-units --type=socket --state=listening --no-pager --no-legend | head -15 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
SOCKET_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
|
||||||
|
printf " ${BRIGHT_MAGENTA}🔌${RESET} ${BRIGHT_WHITE}%-40s${RESET} ${CYAN}%s${RESET}\n" "$SOCKET_NAME" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块10: Target 目标单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_target() {
|
||||||
|
print_section "🎯 Target 目标单元"
|
||||||
|
|
||||||
|
ACTIVE_TARGETS=$(systemctl list-units --type=target --state=active --no-legend | wc -l)
|
||||||
|
print_subitem "当前激活的 Target" "$ACTIVE_TARGETS" "${LIME}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}重要的 Target 单元:${RESET}"
|
||||||
|
IMPORTANT_TARGETS=("default.target" "multi-user.target" "graphical.target" "basic.target" "rescue.target" "emergency.target" "network.target" "sysinit.target")
|
||||||
|
|
||||||
|
for target in "${IMPORTANT_TARGETS[@]}"; do
|
||||||
|
TARGET_STATE=$(systemctl is-active "$target" 2>/dev/null)
|
||||||
|
TARGET_ENABLED=$(systemctl is-enabled "$target" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$TARGET_STATE" ]; then
|
||||||
|
case $TARGET_STATE in
|
||||||
|
active) STATE_ICON="✅"; STATE_COLOR="${BRIGHT_GREEN}" ;;
|
||||||
|
inactive) STATE_ICON="⭕"; STATE_COLOR="${BRIGHT_YELLOW}" ;;
|
||||||
|
*) STATE_ICON="❓"; STATE_COLOR="${BRIGHT_WHITE}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf " ${STATE_ICON} ${BRIGHT_WHITE}%-25s${RESET} ${STATE_COLOR}状态:%-10s${RESET} ${CYAN}启用:%s${RESET}\n" "$target" "$TARGET_STATE" "$TARGET_ENABLED"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块11: Mount 和 Automount 单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_mount() {
|
||||||
|
print_section "📁 挂载点(Mount)信息"
|
||||||
|
|
||||||
|
TOTAL_MOUNTS=$(systemctl list-units --type=mount --all --no-legend | wc -l)
|
||||||
|
ACTIVE_MOUNTS=$(systemctl list-units --type=mount --state=active --no-legend | wc -l)
|
||||||
|
TOTAL_AUTOMOUNTS=$(systemctl list-units --type=automount --all --no-legend | wc -l)
|
||||||
|
ACTIVE_AUTOMOUNTS=$(systemctl list-units --type=automount --state=active --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "挂载点总数" "$TOTAL_MOUNTS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃挂载点" "$ACTIVE_MOUNTS" "${LIME}"
|
||||||
|
print_subitem "自动挂载总数" "$TOTAL_AUTOMOUNTS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃自动挂载" "$ACTIVE_AUTOMOUNTS" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_MOUNTS" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}挂载点详情 (Top 10):${RESET}"
|
||||||
|
systemctl list-units --type=mount --state=active --no-pager --no-legend | head -10 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
MOUNT_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
MOUNT_POINT=$(systemctl show "$MOUNT_NAME" --property=Where --value 2>/dev/null)
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
|
||||||
|
printf " ${BRIGHT_CYAN}📂${RESET} ${BRIGHT_WHITE}%-35s${RESET} ${PURPLE}%s${RESET}\n" "$MOUNT_POINT" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块12: Path 单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_path() {
|
||||||
|
print_section "📍 Path 路径监控单元"
|
||||||
|
|
||||||
|
TOTAL_PATHS=$(systemctl list-units --type=path --all --no-legend | wc -l)
|
||||||
|
ACTIVE_PATHS=$(systemctl list-units --type=path --state=active --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "总 Path 数" "$TOTAL_PATHS" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃 Path" "$ACTIVE_PATHS" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_PATHS" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}活跃的 Path 监控 (Top 10):${RESET}"
|
||||||
|
systemctl list-units --type=path --state=active --no-pager --no-legend | head -10 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
PATH_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
PATH_PATH=$(systemctl show "$PATH_NAME" --property=PathExists --value 2>/dev/null)
|
||||||
|
|
||||||
|
printf " ${BRIGHT_CYAN}📍${RESET} ${BRIGHT_WHITE}%-40s${RESET} ${CYAN}监控:${RESET} ${LIME}%s${RESET} ${CYAN}状态:%s${RESET}\n" "$PATH_NAME" "$PATH_PATH" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块13: Device 单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_device() {
|
||||||
|
print_section "🔧 Device 设备单元"
|
||||||
|
|
||||||
|
TOTAL_DEVICES=$(systemctl list-units --type=device --all --no-legend | wc -l)
|
||||||
|
ACTIVE_DEVICES=$(systemctl list-units --type=device --state=active --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "总设备数" "$TOTAL_DEVICES" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃设备" "$ACTIVE_DEVICES" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_DEVICES" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}活跃的设备 (Top 10):${RESET}"
|
||||||
|
systemctl list-units --type=device --state=active --no-pager --no-legend | head -10 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
DEVICE_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
|
||||||
|
printf " ${BRIGHT_YELLOW}🔧${RESET} ${BRIGHT_WHITE}%-45s${RESET} ${LIME}%s${RESET}\n" "$DEVICE_NAME" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块14: Scope 和 Slice 单元
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_scope_slice() {
|
||||||
|
print_section "📊 Scope 和 Slice 资源控制单元"
|
||||||
|
|
||||||
|
# Scope 单元
|
||||||
|
TOTAL_SCOPES=$(systemctl list-units --type=scope --all --no-legend | wc -l)
|
||||||
|
ACTIVE_SCOPES=$(systemctl list-units --type=scope --state=running --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "Scope 总数" "$TOTAL_SCOPES" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "运行中 Scope" "$ACTIVE_SCOPES" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_SCOPES" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}运行中的 Scope (Top 10):${RESET}"
|
||||||
|
systemctl list-units --type=scope --state=running --no-pager --no-legend | head -10 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
SCOPE_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
|
||||||
|
printf " ${BRIGHT_CYAN}📊${RESET} ${BRIGHT_WHITE}%-45s${RESET} ${LIME}%s${RESET}\n" "$SCOPE_NAME" "$SUB_STATE"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Slice 单元
|
||||||
|
echo ""
|
||||||
|
TOTAL_SLICES=$(systemctl list-units --type=slice --all --no-legend | wc -l)
|
||||||
|
ACTIVE_SLICES=$(systemctl list-units --type=slice --state=active --no-legend | wc -l)
|
||||||
|
|
||||||
|
print_subitem "Slice 总数" "$TOTAL_SLICES" "${BRIGHT_WHITE}"
|
||||||
|
print_subitem "活跃 Slice" "$ACTIVE_SLICES" "${LIME}"
|
||||||
|
|
||||||
|
if [ "$ACTIVE_SLICES" -gt 0 ]; then
|
||||||
|
echo -e "${BRIGHT_CYAN}活跃的 Slice:${RESET}"
|
||||||
|
systemctl list-units --type=slice --state=active --no-pager --no-legend | head -10 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
SLICE_NAME=$(echo "$line" | awk '{print $1}')
|
||||||
|
SUB_STATE=$(echo "$line" | awk '{print $4}')
|
||||||
|
MEMORY=$(systemctl show "$SLICE_NAME" --property=MemoryCurrent --value 2>/dev/null)
|
||||||
|
if [ -n "$MEMORY" ] && [ "$MEMORY" != "[not set]" ]; then
|
||||||
|
MEMORY_DISPLAY="内存: $(numfmt --to=iec $MEMORY 2>/dev/null || echo $MEMORY)"
|
||||||
|
else
|
||||||
|
MEMORY_DISPLAY=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf " ${BRIGHT_MAGENTA}📦${RESET} ${BRIGHT_WHITE}%-30s${RESET} ${LIME}%s${RESET} %s\n" "$SLICE_NAME" "$SUB_STATE" "$MEMORY_DISPLAY"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块15: 依赖关系
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_dependencies() {
|
||||||
|
print_section "🔗 系统依赖关系"
|
||||||
|
|
||||||
|
# 显示默认.target的依赖树
|
||||||
|
DEFAULT_TARGET=$(systemctl get-default)
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}默认目标 '$DEFAULT_TARGET' 的依赖 (前15个):${RESET}"
|
||||||
|
systemctl list-dependencies "$DEFAULT_TARGET" --no-pager --no-legend | head -15 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
UNIT_TYPE=$(echo "$line" | grep -o '\.[a-z]*$' | tr -d '.')
|
||||||
|
case "$UNIT_TYPE" in
|
||||||
|
service) ICON="🔌" ;;
|
||||||
|
target) ICON="🎯" ;;
|
||||||
|
timer) ICON="⏰" ;;
|
||||||
|
socket) ICON="🔌" ;;
|
||||||
|
mount) ICON="📁" ;;
|
||||||
|
path) ICON="📍" ;;
|
||||||
|
*) ICON="📄" ;;
|
||||||
|
esac
|
||||||
|
printf " ${ICON} ${BRIGHT_WHITE}%s${RESET}\n" "$line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 显示被依赖最多的服务
|
||||||
|
echo ""
|
||||||
|
echo -e "${BRIGHT_CYAN}系统关键.target的依赖数量:${RESET}"
|
||||||
|
for target in "multi-user.target" "graphical.target" "basic.target" "network.target"; do
|
||||||
|
DEP_COUNT=$(systemctl list-dependencies "$target" --no-legend 2>/dev/null | wc -l)
|
||||||
|
if [ -n "$DEP_COUNT" ]; then
|
||||||
|
print_subitem "$target" "$DEP_COUNT 个依赖" "${BRIGHT_CYAN}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块16: Systemd 日志信息
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_journal() {
|
||||||
|
print_section "📝 Systemd Journal 日志摘要"
|
||||||
|
|
||||||
|
JOURNAL_SIZE=$(journalctl --disk-usage | grep "Journals use" | awk '{print $3,$4}')
|
||||||
|
JOURNAL_ENTRIES=$(journalctl --no-pager -n 0 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
print_subitem "日志磁盘占用" "$JOURNAL_SIZE" "${ORANGE}"
|
||||||
|
print_subitem "日志总条目" "$JOURNAL_ENTRIES" "${BRIGHT_CYAN}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}最近的错误日志 (最近5条):${RESET}"
|
||||||
|
journalctl -p err -n 5 --no-pager 2>/dev/null | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
echo -e " ${RED}✗${RESET} ${WHITE}${line}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}最近的警告日志 (最近3条):${RESET}"
|
||||||
|
journalctl -p warning -n 3 --no-pager 2>/dev/null | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
echo -e " ${YELLOW}⚠${RESET} ${WHITE}${line}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块17: Systemd 环境变量
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_environment() {
|
||||||
|
print_section "🔧 Systemd 环境变量"
|
||||||
|
|
||||||
|
ENV_COUNT=$(systemctl show-environment 2>/dev/null | wc -l)
|
||||||
|
print_subitem "环境变量数量" "$ENV_COUNT" "${BRIGHT_CYAN}"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_CYAN}系统环境变量:${RESET}"
|
||||||
|
systemctl show-environment 2>/dev/null | head -15 | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
KEY=$(echo "$line" | cut -d'=' -f1)
|
||||||
|
VALUE=$(echo "$line" | cut -d'=' -f2-)
|
||||||
|
echo -e " ${BRIGHT_YELLOW}●${RESET} ${BRIGHT_CYAN}${KEY}${RESET}=${BRIGHT_WHITE}${VALUE}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块18: Cgroup 信息
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_cgroup() {
|
||||||
|
print_section "🧊 Cgroup 信息"
|
||||||
|
|
||||||
|
# 获取 cgroup 版本
|
||||||
|
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
|
||||||
|
CGROUP_VERSION="v2 (unified)"
|
||||||
|
else
|
||||||
|
CGROUP_VERSION="v1 (legacy)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_subitem "Cgroup 版本" "$CGROUP_VERSION" "${BRIGHT_CYAN}"
|
||||||
|
|
||||||
|
# 获取控制器信息
|
||||||
|
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
|
||||||
|
CONTROLLERS=$(cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null | tr ' ' ', ')
|
||||||
|
else
|
||||||
|
CONTROLLERS=$(cat /sys/fs/cgroup/devices.list 2>/dev/null | head -1 | cut -d' ' -f1 || echo "N/A")
|
||||||
|
fi
|
||||||
|
print_subitem "可用控制器" "$CONTROLLERS" "${LIME}"
|
||||||
|
|
||||||
|
# Slice 资源统计
|
||||||
|
echo -e "${BRIGHT_CYAN}Slice 资源使用 (Top 5):${RESET}"
|
||||||
|
for slice in $(systemctl list-units --type=slice --state=active --no-legend | awk '{print $1}' | head -5); do
|
||||||
|
MEM_CURRENT=$(systemctl show "$slice" --property=MemoryCurrent --value 2>/dev/null)
|
||||||
|
MEM_MAX=$(systemctl show "$slice" --property=MemoryMax --value 2>/dev/null)
|
||||||
|
CPU_WEIGHT=$(systemctl show "$slice" --property=CPUWeight --value 2>/dev/null)
|
||||||
|
|
||||||
|
MEM_DISP=""
|
||||||
|
if [ -n "$MEM_CURRENT" ] && [ "$MEM_CURRENT" != "[not set]" ]; then
|
||||||
|
MEM_DISP="内存: $(numfmt --to=iec $MEM_CURRENT 2>/dev/null || echo $MEM_CURRENT)"
|
||||||
|
fi
|
||||||
|
if [ -n "$CPU_WEIGHT" ] && [ "$CPU_WEIGHT" != "[not set]" ]; then
|
||||||
|
MEM_DISP="$MEM_DISP CPU权重: $CPU_WEIGHT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$MEM_DISP" ]; then
|
||||||
|
printf " ${BRIGHT_MAGENTA}📦${RESET} ${BRIGHT_WHITE}%-25s${RESET} ${LIME}%s${RESET}\n" "$slice" "$MEM_DISP"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块19: 系统性能信息
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_performance() {
|
||||||
|
print_section "📊 系统性能信息"
|
||||||
|
|
||||||
|
# 获取 CPU 使用率
|
||||||
|
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
|
||||||
|
|
||||||
|
# 获取内存信息
|
||||||
|
MEM_INFO=$(free -h | grep "Mem:")
|
||||||
|
MEM_TOTAL=$(echo "$MEM_INFO" | awk '{print $2}')
|
||||||
|
MEM_USED=$(echo "$MEM_INFO" | awk '{print $3}')
|
||||||
|
MEM_FREE=$(echo "$MEM_INFO" | awk '{print $4}')
|
||||||
|
MEM_PERCENT=$(free | grep "Mem:" | awk '{printf "%.1f", $3/$2*100}')
|
||||||
|
|
||||||
|
# 获取启动时间
|
||||||
|
BOOT_TIME_SEC=$(systemctl show --property=UserspaceTimestampMonotonic --value | cut -d' ' -f1)
|
||||||
|
BOOT_TIME_SEC=${BOOT_TIME_SEC:-0}
|
||||||
|
BOOT_TIME_SEC=$((BOOT_TIME_SEC / 1000000))
|
||||||
|
|
||||||
|
print_subitem "CPU 使用率" "${CPU_USAGE}%" "${BRIGHT_YELLOW}"
|
||||||
|
print_subitem "内存总量" "$MEM_TOTAL" "${BRIGHT_CYAN}"
|
||||||
|
print_subitem "已用内存" "$MEM_USED (${MEM_PERCENT}%)" "${ORANGE}"
|
||||||
|
print_subitem "可用内存" "$MEM_FREE" "${LIME}"
|
||||||
|
print_subitem "启动耗时" "${BOOT_TIME_SEC} 秒" "${PURPLE}"
|
||||||
|
|
||||||
|
# Swap 信息
|
||||||
|
SWAP_TOTAL=$(free -h | grep "Swap:" | awk '{print $2}')
|
||||||
|
SWAP_USED=$(free -h | grep "Swap:" | awk '{print $3}')
|
||||||
|
SWAP_FREE=$(free -h | grep "Swap:" | awk '{print $4}')
|
||||||
|
|
||||||
|
if [ "$SWAP_TOTAL" != "0" ]; then
|
||||||
|
print_subitem "Swap总量" "$SWAP_TOTAL" "${PINK}"
|
||||||
|
print_subitem "Swap已用" "$SWAP_USED" "${PINK}"
|
||||||
|
print_subitem "Swap可用" "$SWAP_FREE" "${PINK}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块20: 电源管理状态
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_power_management() {
|
||||||
|
print_section "🔋 电源管理状态"
|
||||||
|
|
||||||
|
# 检查 systemd-suspend 服务
|
||||||
|
if systemctl list-unit-files | grep -q "systemd-suspend.service"; then
|
||||||
|
SUSPEND_STATE=$(systemctl is-enabled systemd-suspend.service 2>/dev/null || echo "N/A")
|
||||||
|
print_subitem "Suspend 服务" "$SUSPEND_STATE" "${BRIGHT_CYAN}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 systemd-hibernate 服务
|
||||||
|
if systemctl list-unit-files | grep -q "systemd-hibernate.service"; then
|
||||||
|
HIBERNATE_STATE=$(systemctl is-enabled systemd-hibernate.service 2>/dev/null || echo "N/A")
|
||||||
|
print_subitem "Hibernate 服务" "$HIBERNATE_STATE" "${BRIGHT_CYAN}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查 logind 状态
|
||||||
|
if systemctl list-unit-files | grep -q "systemd-logind.service"; then
|
||||||
|
LOGIND_STATE=$(systemctl is-active systemd-logind.service 2>/dev/null || echo "N/A")
|
||||||
|
print_subitem "Logind 状态" "$LOGIND_STATE" "${LIME}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 显示电源相关事件
|
||||||
|
echo -e "${BRIGHT_CYAN}最近的电源相关日志:${RESET}"
|
||||||
|
journalctl -u systemd-logind -u upower -n 3 --no-pager 2>/dev/null | while IFS= read -r line; do
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
echo -e " ${CYAN}▸${RESET} ${WHITE}${line}${RESET}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块21: 关键服务状态
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_critical_services() {
|
||||||
|
print_section "🔑 关键系统服务状态"
|
||||||
|
|
||||||
|
KEY_SERVICES=(
|
||||||
|
"sshd.service"
|
||||||
|
"NetworkManager.service"
|
||||||
|
"cron.service"
|
||||||
|
"rsyslog.service"
|
||||||
|
"dbus.service"
|
||||||
|
"systemd-logind.service"
|
||||||
|
"systemd-journald.service"
|
||||||
|
"systemd-udevd.service"
|
||||||
|
"polkit.service"
|
||||||
|
)
|
||||||
|
|
||||||
|
for service in "${KEY_SERVICES[@]}"; do
|
||||||
|
if systemctl list-unit-files 2>/dev/null | grep -q "$service"; then
|
||||||
|
SERVICE_STATE=$(systemctl is-active "$service" 2>/dev/null)
|
||||||
|
SERVICE_ENABLED=$(systemctl is-enabled "$service" 2>/dev/null)
|
||||||
|
|
||||||
|
case $SERVICE_STATE in
|
||||||
|
active) STATE_ICON="✅"; STATE_COLOR="${BRIGHT_GREEN}" ;;
|
||||||
|
inactive) STATE_ICON="⭕"; STATE_COLOR="${BRIGHT_YELLOW}" ;;
|
||||||
|
failed) STATE_ICON="❌"; STATE_COLOR="${BRIGHT_RED}" ;;
|
||||||
|
*) STATE_ICON="❓"; STATE_COLOR="${BRIGHT_WHITE}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf " ${STATE_ICON} ${BRIGHT_WHITE}%-30s${RESET} ${STATE_COLOR}%-10s${RESET} ${CYAN}启用:%s${RESET}\n" "$service" "$SERVICE_STATE" "$SERVICE_ENABLED"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 模块22: 常用命令提示
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
module_help() {
|
||||||
|
print_section "💡 常用 Systemctl 命令"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_YELLOW}=== 服务管理 ===${RESET}"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl status <service>${RESET} - 查看服务状态"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl start <service>${RESET} - 启动服务"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl stop <service>${RESET} - 停止服务"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl restart <service>${RESET} - 重启服务"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl enable <service>${RESET} - 启用开机自启"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl disable <service>${RESET} - 禁用开机自启"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl mask <service>${RESET} - 屏蔽服务"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl unmask <service>${RESET} - 取消屏蔽"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_YELLOW}=== 状态查看 ===${RESET}"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl is-active <service>${RESET} - 检查服务是否活跃"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl is-enabled <service>${RESET} - 检查服务是否启用"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl --failed${RESET} - 查看失败的服务"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl list-dependencies <unit>${RESET} - 查看依赖"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_YELLOW}=== 日志查看 ===${RESET}"
|
||||||
|
echo -e "${BRIGHT_WHITE}journalctl -u <service>${RESET} - 查看服务日志"
|
||||||
|
echo -e "${BRIGHT_WHITE}journalctl -xe${RESET} - 查看最近日志"
|
||||||
|
echo -e "${BRIGHT_WHITE}journalctl -p err${RESET} - 查看错误日志"
|
||||||
|
|
||||||
|
echo -e "${BRIGHT_YELLOW}=== 电源管理 ===${RESET}"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl suspend${RESET} - 挂起"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl hibernate${RESET} - 休眠"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl reboot${RESET} - 重启"
|
||||||
|
echo -e "${BRIGHT_WHITE}systemctl poweroff${RESET} - 关机"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
# 主函数 - 模块调度
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
main() {
|
||||||
|
print_header
|
||||||
|
|
||||||
|
# 基础信息模块
|
||||||
|
module_systemd_version
|
||||||
|
module_system_info
|
||||||
|
module_systemd_status
|
||||||
|
|
||||||
|
# 单元统计模块
|
||||||
|
module_service_stats
|
||||||
|
module_running_services
|
||||||
|
module_failed_services
|
||||||
|
module_masked_services
|
||||||
|
|
||||||
|
# 各类单元模块
|
||||||
|
module_timer
|
||||||
|
module_socket
|
||||||
|
module_target
|
||||||
|
module_mount
|
||||||
|
module_path
|
||||||
|
module_device
|
||||||
|
module_scope_slice
|
||||||
|
|
||||||
|
# 系统信息模块
|
||||||
|
module_dependencies
|
||||||
|
module_journal
|
||||||
|
module_environment
|
||||||
|
module_cgroup
|
||||||
|
module_performance
|
||||||
|
module_power_management
|
||||||
|
|
||||||
|
# 服务状态模块
|
||||||
|
module_critical_services
|
||||||
|
|
||||||
|
# 帮助信息
|
||||||
|
module_help
|
||||||
|
|
||||||
|
# 结束
|
||||||
|
echo -e "${DASH_SEPARATOR}"
|
||||||
|
echo -e "${BRIGHT_MAGENTA}✨ 信息收集完成!时间: $(date '+%Y-%m-%d %H:%M:%S') ✨${RESET}"
|
||||||
|
echo -e "${DASH_SEPARATOR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行主函数
|
||||||
|
main "@"
|
||||||
1219
user-manager/user-manager.sh
Executable file
1219
user-manager/user-manager.sh
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user