Files
linux-bash/login-info/login-info.sh
2026-02-17 17:28:37 +08:00

268 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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}"