1. Shell脚本编程基础与模板定制
1.1 Shell脚本开发环境准备
在开始编写Shell脚本前,我们需要确保开发环境配置正确。对于OpenEuler或主流Linux发行版,通常已经预装了Bash解释器。可以通过以下命令检查:
bash复制ls -l /bin/bash
which bash
bash --version
注意:虽然大多数Linux系统默认使用Bash,但不同发行版可能有差异。建议明确在脚本首行指定
#!/bin/bash以确保兼容性。
1.2 脚本模板工具详解
原始内容中展示的newsc1模板生成工具非常实用,我们来深入解析其实现原理:
bash复制#!/bin/bash
# 获取当前年份
CURRENT_YEAR=$(date +%Y)
CURRENT_DATE=$(date +'%Y年%m月%d日')
CURRENT_USER=$(whoami)
# 文件名处理逻辑
if [[ ! "$script_name" =~ \.sh$ ]]; then
script_name="${script_name}.sh"
fi
关键点解析:
date +%Y获取四位数年份,+%m和+%d分别获取月份和日期whoami获取当前用户名,比$USER环境变量更可靠- 正则表达式
=~ \.sh$检查文件名是否以.sh结尾
1.3 增强版模板工具实现
基于原始脚本,我们可以增加更多实用功能:
bash复制# 增加参数校验
if [ -z "$script_name" ]; then
echo "错误:文件名不能为空"
exit 1
fi
# 增加模板选择功能
echo "请选择模板类型:"
echo "1) 基础模板"
echo "2) 带参数检查的模板"
read -p "输入选择(默认1): " template_type
case "${template_type:-1}" in
1) template_file="basic_template.sh" ;;
2) template_file="param_check_template.sh" ;;
*) echo "无效选择,使用默认模板"; template_file="basic_template.sh" ;;
esac
# 从模板文件生成
if [ -f "/usr/local/share/templates/$template_file" ]; then
cat "/usr/local/share/templates/$template_file" > "$script_name"
else
# 默认模板内容
cat > "$script_name" << EOF
#!/bin/bash
# ================================================
# Script Name: $script_name
# Description:
# Author: $CURRENT_USER
# Created: $CURRENT_DATE
# Version: v0.1
# Usage: $script_name [options]
# Options:
# -h 显示帮助信息
# -v 显示版本信息
# ================================================
EOF
fi
实操技巧:将模板工具安装在
/usr/local/bin/并设置755权限,这样所有用户都可以使用。建议配套创建模板目录/usr/local/share/templates/存放不同类型的模板文件。
2. Shell脚本基础命令实战
2.1 echo命令高级用法
原始内容中演示了基础的echo输出,实际上echo有很多实用参数:
bash复制# 输出彩色文本
echo -e "\033[31m红色文字\033[0m"
echo -e "\033[32;44m绿字蓝底\033[0m"
# 输出不换行
echo -n "正在处理..."
sleep 1
echo "完成"
# 特殊字符处理
echo "这是一行\
跨越两行的文本"
echo "包含\"引号\"的文本"
ANSI颜色代码参考:
\033[31m红色\033[32m绿色\033[33m黄色\033[0m重置样式
2.2 菜单系统实现进阶
基于原始菜单示例,我们可以开发更完整的交互系统:
bash复制#!/bin/bash
# version: 2.1
show_menu() {
clear
echo -e "\033[44m========== 系统管理菜单 ==========\033[0m"
echo -e "\033[32m 1. 查看网卡信息\033[0m"
echo -e "\033[33m 2. 查看内存信息\033[0m"
echo -e "\033[34m 3. 查看磁盘信息\033[0m"
echo -e "\033[35m 4. 查看CPU信息\033[0m"
echo -e "\033[36m 5. 退出程序\033[0m"
echo -e "\033[44m==================================\033[0m"
}
process_choice() {
case $1 in
1) ip addr show ;;
2) free -h ;;
3) df -h ;;
4) lscpu ;;
5) exit 0 ;;
*) echo "无效选择,请重新输入" ;;
esac
read -p "按Enter键继续..."
}
while true; do
show_menu
read -p "请输入选项(1-5): " choice
process_choice "$choice"
done
关键改进点:
- 使用函数组织代码,提高可读性
- 增加循环实现菜单持续显示
- 每个功能执行后暂停等待用户确认
- 添加退出选项
3. 系统管理实战脚本
3.1 批量用户创建脚本解析
原始内容中的批量用户创建脚本非常实用,我们来分析其关键部分:
bash复制# 组创建检查
groupadd -f "$GROUP_NAME" || {
echo "创建组 $GROUP_NAME 失败"
exit 1
}
# 用户创建主循环
while IFS= read -r line; do
[ -z "$line" ] && continue
id=$(echo "$line" | awk '{print $1}')
name=$(echo "$line" | awk '{print $2}')
# 检查用户是否已存在
if id "$name" &>/dev/null; then
echo "用户 $name 已存在,跳过创建"
continue
fi
# 创建用户并设置密码
if ! useradd -m -s /bin/bash "$name"; then
echo "创建用户 $name 失败"
continue
fi
echo "$name:$id" | chpasswd || echo "设置密码失败"
usermod -aG "$GROUP_NAME" "$name"
# 强制首次登录修改密码
chage -d 0 "$name"
echo "已创建用户: $name (学号: $id)"
done < "$INPUT_FILE"
增强功能说明:
- 增加用户存在性检查
- 添加错误处理逻辑
- 设置首次登录强制修改密码
- 优化执行过程反馈
3.2 系统信息查询脚本优化
原始系统信息查询脚本可以进一步扩展:
bash复制#!/bin/bash
# version: 2.0
get_system_info() {
# 主机信息
local hostname=$(hostname -f)
local os_info=$(source /etc/os-release; echo "$PRETTY_NAME")
# 网络信息
local interfaces=($(ip -o link show | awk -F': ' '{print $2}' | grep -v lo))
local ip_info=""
for intf in "${interfaces[@]}"; do
ip=$(ip -4 addr show "$intf" | grep 'inet ' | awk '{print $2}' | cut -d/ -f1)
[ -n "$ip" ] && ip_info+="$intf: $ip"$'\n'
done
# 内存信息
local mem_total=$(free -h | awk '/Mem:/ {print $2}')
local mem_used=$(free -h | awk '/Mem:/ {print $3}')
local mem_free=$(free -h | awk '/Mem:/ {print $4}')
# CPU信息
local cpu_model=$(lscpu | grep "Model name" | cut -d: -f2 | sed 's/^[ \t]*//')
local cpu_cores=$(nproc)
local load_avg=$(uptime | awk -F'average: ' '{print $2}')
# 输出报告
cat << EOF
============== 系统信息报告 ==============
主机名: $hostname
操作系统: $os_info
----- 网络信息 -----
${ip_info}
----- 内存使用 -----
总内存: $mem_total
已使用: $mem_used
空闲内存: $mem_free
----- CPU信息 -----
型号: $cpu_model
核心数: $cpu_cores
系统负载: $load_avg
=======================================
EOF
}
get_system_info
新增功能点:
- 获取完整主机名和OS详细信息
- 自动检测所有网络接口IP
- 显示更详细的内存使用情况
- 添加CPU型号和核心数信息
- 格式化输出为完整报告
4. Shell脚本调试与优化
4.1 常见错误排查技巧
在脚本开发过程中,经常会遇到各种问题,以下是一些实用调试技巧:
- 语法检查:
bash复制bash -n script.sh # 只检查语法不执行
- 详细执行跟踪:
bash复制bash -x script.sh # 显示每行命令执行情况
- 变量检查:
bash复制# 在脚本中插入调试语句
echo "DEBUG: 变量值: var1=$var1, var2=$var2" >&2
- 错误处理增强:
bash复制# 设置严格模式
set -euo pipefail
# 自定义错误处理
trap 'echo "错误发生在第 $LINENO 行"; exit 1' ERR
4.2 脚本性能优化建议
- 减少子进程调用:
bash复制# 不好的做法:多次调用awk
data=$(cat file.txt | awk '{print $1}' | awk -F':' '{print $2}')
# 好的做法:一次awk处理
data=$(awk -F'[: ]' '{print $2}' file.txt)
- 使用内置字符串处理:
bash复制# 替代grep+cut组合
# 原始方式
ip=$(ip addr | grep 'inet ' | cut -d' ' -f6 | cut -d/ -f1)
# 优化方式
ip=$(ip -4 addr show eth0 | awk '/inet / {split($2,a,"/"); print a[1]}')
- 批量操作替代循环:
bash复制# 批量创建用户(适用于已知用户名列表)
echo "user1:password1
user2:password2" | newusers
4.3 环境变量最佳实践
原始内容中演示了基础环境变量使用,实际开发中应注意:
- 变量引用安全:
bash复制# 不安全,如果变量包含空格或特殊字符会出问题
rm -rf $DIR/*
# 安全做法
rm -rf "${DIR:?}"/*
- 重要变量保护:
bash复制# 设置只读变量
readonly MAX_RETRY=3
- 变量默认值:
bash复制# 如果变量未设置,使用默认值
LOG_LEVEL="${LOG_LEVEL:-info}"
- 环境变量清理:
bash复制# 临时修改环境变量
(
export PATH="/custom/bin:$PATH"
custom_command
)
# 此处PATH已恢复原值
5. 实战案例:自动化运维脚本开发
5.1 系统健康检查脚本
结合原始内容中的系统查询命令,我们可以开发完整的健康检查脚本:
bash复制#!/bin/bash
# version: 1.0
# 系统健康检查工具
# 阈值配置
CPU_WARNING=80 # CPU使用率警告阈值(%)
MEM_WARNING=90 # 内存使用率警告阈值(%)
DISK_WARNING=90 # 磁盘使用率警告阈值(%)
check_cpu() {
local usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
if (( $(echo "$usage > $CPU_WARNING" | bc -l) )); then
echo "[警告] CPU使用率过高: ${usage}%"
return 1
fi
echo "[正常] CPU使用率: ${usage}%"
return 0
}
check_memory() {
local total=$(free -m | awk '/Mem:/ {print $2}')
local used=$(free -m | awk '/Mem:/ {print $3}')
local usage=$((used*100/total))
if [ $usage -ge $MEM_WARNING ]; then
echo "[警告] 内存使用率过高: ${usage}%"
return 1
fi
echo "[正常] 内存使用率: ${usage}%"
return 0
}
check_disk() {
local result=""
while IFS= read -r line; do
local partition=$(echo "$line" | awk '{print $1}')
local usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
if [ $usage -ge $DISK_WARNING ]; then
result+="[警告] 磁盘 ${partition} 使用率过高: ${usage}%"$'\n'
else
result+="[正常] 磁盘 ${partition} 使用率: ${usage}%"$'\n'
fi
done < <(df -h | awk 'NR>1 {print $1,$5}')
echo "$result"
}
main() {
echo "====== 系统健康检查报告 ======"
echo "检查时间: $(date)"
echo
echo "----- CPU检查 -----"
check_cpu
echo -e "\n----- 内存检查 -----"
check_memory
echo -e "\n----- 磁盘检查 -----"
check_disk
echo -e "\n====== 检查完成 ======"
}
main
5.2 日志分析脚本示例
bash复制#!/bin/bash
# 日志分析工具
analyze_log() {
local log_file="$1"
local output_file="${log_file}.analysis"
echo "开始分析日志文件: $log_file"
# 统计错误级别
echo "===== 错误级别统计 =====" > "$output_file"
grep -oP '(?<=level=)\w+' "$log_file" | sort | uniq -c >> "$output_file"
# 提取前10个错误
echo -e "\n===== 前10个错误 =====" >> "$output_file"
grep -i "error" "$log_file" | head -n 10 >> "$output_file"
# 请求耗时统计
if grep -q "duration=" "$log_file"; then
echo -e "\n===== 请求耗时分布 =====" >> "$output_file"
awk -F'duration=' '{print $2}' "$log_file" | awk '{print $1}' | \
awk '{if($1<0.1) a++; else if($1<0.5) b++; else if($1<1) c++; else d++}
END {print "<0.1s: "a"\n0.1-0.5s: "b"\n0.5-1s: "c"\n>1s: "d}' >> "$output_file"
fi
echo "分析完成,结果保存在: $output_file"
}
# 主程序
if [ $# -eq 0 ]; then
echo "用法: $0 日志文件 [日志文件...]"
exit 1
fi
for log in "$@"; do
[ -f "$log" ] || {
echo "警告: 文件 $log 不存在,跳过"
continue
}
analyze_log "$log"
done
6. Shell脚本安全实践
6.1 安全编程要点
- 输入验证:
bash复制# 检查参数是否为数字
if ! [[ "$1" =~ ^[0-9]+$ ]]; then
echo "错误:参数必须是数字"
exit 1
fi
- 权限控制:
bash复制# 检查root权限
[ "$(id -u)" -eq 0 ] || {
echo "需要root权限执行此脚本"
exit 1
}
- 安全执行外部命令:
bash复制# 安全执行方式
cmd="rm"
args=("-rf" "/tmp/test")
"$cmd" "${args[@]}"
6.2 敏感信息处理
- 密码安全处理:
bash复制# 使用openssl生成随机密码
PASSWORD=$(openssl rand -base64 12)
# 交互式安全输入
read -s -p "输入密码: " password
echo
- 配置文件安全:
bash复制# 配置文件权限设置
chmod 600 /path/to/config
chown root:root /path/to/config
- 临时文件安全:
bash复制# 安全创建临时文件
temp_file=$(mktemp /tmp/secure_XXXXXX)
trap 'rm -f "$temp_file"' EXIT