函数是Shell脚本编程中实现代码重用和模块化的核心工具。通过函数,我们可以将复杂的脚本分解为小的、可管理的部分,提高代码的可读性、可维护性和重用性。
将常用功能封装为函数,避免代码重复。
将复杂任务分解为小的、专注的函数。
通过函数名表达意图,提高代码可读性。
修改功能只需修改一个地方。
可以单独测试和调试每个函数。
函数可以调用自身,解决复杂问题。
函数是编写高质量Shell脚本的基础。通过合理的函数设计,我们可以创建模块化、可维护、可重用的脚本,大大提高开发效率和代码质量。函数还有助于团队协作,因为不同的开发者可以负责不同的函数模块。
学习如何定义函数和调用函数。
#!/bin/bash
# 函数定义和调用示例
echo "=== 基本函数定义 ==="
# 方法1: 使用function关键字
function say_hello {
echo "Hello, World!"
}
# 方法2: 使用圆括号
greet_user() {
local name="$1"
echo "Hello, $name!"
}
# 方法3: 组合写法
function show_date() {
echo "当前日期: $(date)"
}
# 调用函数
echo "调用 say_hello 函数:"
say_hello
echo -e "\n调用 greet_user 函数:"
greet_user "Alice"
echo -e "\n调用 show_date 函数:"
show_date
echo -e "\n=== 函数执行顺序 ==="
# 函数可以在定义前调用(在Bash中)
early_call
function early_call {
echo "这个函数在调用之后才定义"
}
echo -e "\n=== 函数重复定义 ==="
# 后面的定义会覆盖前面的定义
duplicate_func() {
echo "第一个定义"
}
duplicate_func() {
echo "第二个定义(覆盖第一个)"
}
duplicate_func
echo -e "\n=== 函数调用其他函数 ==="
function inner_function {
echo "这是内部函数"
}
function outer_function {
echo "这是外部函数,正在调用内部函数:"
inner_function
}
outer_function
echo -e "\n=== 检查函数是否存在 ==="
# 检查函数是否定义
if declare -f non_existent_function > /dev/null; then
echo "函数 non_existent_function 存在"
else
echo "函数 non_existent_function 不存在"
fi
if declare -f say_hello > /dev/null; then
echo "函数 say_hello 存在"
fi
echo -e "\n=== 列出所有函数 ==="
# 显示当前定义的所有函数
echo "当前定义的函数:"
declare -F | cut -d' ' -f3
echo -e "\n=== 函数作为命令 ==="
# 函数可以像普通命令一样使用
create_dir() {
local dir_name="$1"
if [ -z "$dir_name" ]; then
echo "错误: 请提供目录名"
return 1
fi
if mkdir -p "$dir_name"; then
echo "目录创建成功: $dir_name"
else
echo "目录创建失败: $dir_name"
return 1
fi
}
# 使用函数
create_dir "test_directory"
create_dir "" # 这会触发错误
echo -e "\n=== 函数中的局部变量 ==="
global_var="我是全局变量"
function test_scope {
local local_var="我是局部变量"
echo "在函数内: $local_var"
echo "在函数内访问全局变量: $global_var"
}
test_scope
echo "在函数外访问局部变量: ${local_var:-未定义}"
echo "在函数外访问全局变量: $global_var"
echo -e "\n=== 删除函数 ==="
# 使用unset删除函数
function temporary_function {
echo "这是一个临时函数"
}
echo "删除前:"
temporary_function
unset -f temporary_function
echo "删除后:"
if ! declare -f temporary_function > /dev/null; then
echo "函数已被删除"
fi
local关键字定义局部变量学习如何向函数传递参数和处理参数。
| 变量 | 说明 | 示例 |
|---|---|---|
$1, $2, ... |
第1、2...个参数 | echo "第一个参数: $1" |
$# |
参数个数 | echo "参数个数: $#" |
$@ |
所有参数(单独单词) | for arg in "$@"; do |
$* |
所有参数(单个单词) | echo "所有参数: $*" |
$0 |
函数名(在函数内) | echo "函数名: $0" |
#!/bin/bash
# 函数参数处理示例
echo "=== 基本参数处理 ==="
# 简单参数函数
show_params() {
echo "函数名: $0"
echo "参数个数: $#"
echo "所有参数: $@"
if [ $# -gt 0 ]; then
echo "第一个参数: $1"
echo "第二个参数: ${2:-未提供第二个参数}"
else
echo "没有提供参数"
fi
}
echo "调用 show_params:"
show_params
echo -e "\n调用 show_params arg1 arg2 arg3:"
show_params "arg1" "arg2" "arg3"
echo -e "\n=== 参数验证 ==="
# 带参数验证的函数
validate_params() {
if [ $# -lt 2 ]; then
echo "错误: 需要至少2个参数,但只提供了 $# 个"
echo "用法: $0 参数1 参数2 [参数3...]"
return 1
fi
local required="$1"
local optional="${2:-默认值}"
echo "必需参数: $required"
echo "可选参数: $optional"
# 处理剩余参数
shift 2
if [ $# -gt 0 ]; then
echo "额外参数: $@"
fi
}
echo "测试参数验证:"
validate_params
validate_params "必需参数"
validate_params "必需参数" "可选参数" "额外1" "额外2"
echo -e "\n=== 使用shift处理参数 ==="
# 使用shift处理多个参数
process_args() {
echo "开始处理参数..."
local count=1
while [ $# -gt 0 ]; do
echo "参数 $count: $1"
((count++))
shift
done
echo "参数处理完成"
}
echo "调用 process_args:"
process_args "苹果" "香蕉" "橙子" "葡萄"
echo -e "\n=== 命名参数模拟 ==="
# 模拟命名参数(使用选项)
named_params() {
local name=""
local age=""
local verbose=false
# 解析选项
while [[ $# -gt 0 ]]; do
case $1 in
-n|--name)
name="$2"
shift 2
;;
-a|--age)
age="$2"
shift 2
;;
-v|--verbose)
verbose=true
shift
;;
*)
echo "未知选项: $1"
return 1
;;
esac
done
echo "名字: ${name:-未提供}"
echo "年龄: ${age:-未提供}"
if [ "$verbose" = true ]; then
echo "详细模式: 开启"
fi
}
echo "测试命名参数:"
named_params --name "Alice" --age 25 --verbose
named_params -n "Bob" -a 30
echo -e "\n=== 参数默认值 ==="
# 使用默认参数值
create_file() {
local filename="${1:-default.txt}"
local content="${2:-默认内容}"
echo "$content" > "$filename"
echo "文件创建成功: $filename"
}
echo "测试默认参数:"
create_file
create_file "custom.txt"
create_file "message.txt" "这是自定义内容"
echo -e "\n=== 数组作为参数 ==="
# 处理数组参数
process_array() {
local array_name="$1"
shift
local array=("$@")
echo "数组名: $array_name"
echo "数组元素:"
for element in "${array[@]}"; do
echo " - $element"
done
echo "数组长度: ${#array[@]}"
}
fruits=("苹果" "香蕉" "橙子")
echo "测试数组参数:"
process_array "水果" "${fruits[@]}"
echo -e "\n=== 参数引用 ==="
# 通过引用修改参数(使用eval)
increment() {
local var_name="$1"
local current_value=${!var_name}
local new_value=$((current_value + 1))
eval "$var_name=$new_value"
}
counter=5
echo "修改前: counter=$counter"
increment "counter"
echo "修改后: counter=$counter"
echo -e "\n=== 参数类型检查 ==="
# 参数类型验证
validate_number() {
local input="$1"
local param_name="$2"
if ! [[ "$input" =~ ^-?[0-9]+$ ]]; then
echo "错误: $param_name 必须是整数,但得到: $input"
return 1
fi
}
calculate_sum() {
validate_number "$1" "第一个参数" || return 1
validate_number "$2" "第二个参数" || return 1
local sum=$(($1 + $2))
echo "和: $1 + $2 = $sum"
}
echo "测试参数类型检查:"
calculate_sum 10 20
calculate_sum 10 "abc"
shift处理多个参数学习如何处理函数的返回值。
| 方法 | 说明 | 适用场景 |
|---|---|---|
return |
返回退出状态码(0-255) | 表示成功/失败 |
echo |
输出到标准输出 | 返回字符串或数据 |
全局变量 |
修改全局变量 | 返回多个值或复杂数据 |
命令替换 |
使用$(function) |
捕获函数输出 |
#!/bin/bash
# 函数返回值处理示例
echo "=== 使用return返回状态码 ==="
# 返回成功/失败状态
file_exists() {
local filename="$1"
if [ -f "$filename" ]; then
return 0 # 成功
else
return 1 # 失败
fi
}
# 测试文件存在性
echo "测试文件存在性:"
if file_exists "/etc/passwd"; then
echo "文件存在"
else
echo "文件不存在"
fi
if file_exists "/nonexistent/file"; then
echo "文件存在"
else
echo "文件不存在"
fi
# 检查退出状态码
file_exists "/etc/passwd"
echo "退出状态码: $?"
file_exists "/nonexistent/file"
echo "退出状态码: $?"
echo -e "\n=== 使用echo返回数据 ==="
# 返回字符串数据
get_greeting() {
local name="$1"
echo "Hello, $name!"
}
# 捕获函数输出
message=$(get_greeting "Alice")
echo "捕获的消息: $message"
# 直接使用函数输出
echo "直接使用: $(get_greeting "Bob")"
echo -e "\n=== 返回数值计算结果 ==="
# 返回数值计算的结果
calculate_area() {
local radius="$1"
local pi=3.14159
local area=$(echo "$pi * $radius * $radius" | bc)
echo "$area"
}
circle_area=$(calculate_area 5)
echo "半径为5的圆面积: $circle_area"
echo -e "\n=== 返回多个值 ==="
# 方法1: 返回多个值(使用echo和分割)
get_user_info() {
local username="$1"
# 模拟从数据库获取信息
echo "Alice:25:Engineer:London"
}
# 处理多个返回值
user_data=$(get_user_info "Alice")
IFS=':' read -r name age job city <<< "$user_data"
echo "姓名: $name"
echo "年龄: $age"
echo "职业: $job"
echo "城市: $city"
# 方法2: 使用全局变量
global_result=""
set_global_result() {
global_result="这是全局结果"
}
set_global_result
echo "全局变量结果: $global_result"
echo -e "\n=== 返回数组 ==="
# 返回数组
get_fruits() {
local fruits=("apple" "banana" "orange" "grape")
printf "%s\n" "${fruits[@]}"
}
# 捕获数组
fruits_array=($(get_fruits))
echo "水果数组:"
for fruit in "${fruits_array[@]}"; do
echo " - $fruit"
done
echo -e "\n=== 复杂的返回值处理 ==="
# 返回结构化的数据
process_data() {
local input="$1"
# 处理数据...
local processed="${input^^}" # 转换为大写
local length=${#input}
local words=$(echo "$input" | wc -w)
# 返回多个值
echo "$processed"
echo "$length"
echo "$words"
}
# 读取多个返回值
{
read -r processed_data
read -r data_length
read -r word_count
} < <(process_data "hello world")
echo "处理后的数据: $processed_data"
echo "数据长度: $data_length"
echo "单词数: $word_count"
echo -e "\n=== 错误处理和返回值 ==="
# 带错误处理的函数
safe_division() {
local dividend="$1"
local divisor="$2"
# 参数验证
if ! [[ "$dividend" =~ ^-?[0-9]+$ ]] || ! [[ "$divisor" =~ ^-?[0-9]+$ ]]; then
echo "错误: 参数必须是数字" >&2
return 1
fi
if [ "$divisor" -eq 0 ]; then
echo "错误: 除数不能为零" >&2
return 1
fi
local result=$((dividend / divisor))
echo "$result"
return 0
}
echo "测试安全除法:"
division_result=$(safe_division 10 2)
if [ $? -eq 0 ]; then
echo "除法结果: $division_result"
else
echo "除法失败"
fi
error_result=$(safe_division 10 0)
if [ $? -eq 0 ]; then
echo "除法结果: $error_result"
else
echo "除法失败"
fi
echo -e "\n=== 使用函数链 ==="
# 函数链:一个函数的输出作为另一个函数的输入
double() {
local num="$1"
echo $((num * 2))
}
add_five() {
local num="$1"
echo $((num + 5))
}
# 函数组合
result=$(add_five $(double 10))
echo "double(10) + 5 = $result"
# 更复杂的链
result=$(double $(add_five $(double 5)))
echo "double(add_five(double(5))) = $result"
echo -e "\n=== 返回布尔值 ==="
# 返回布尔值(使用退出状态)
is_even() {
local number="$1"
if [ $((number % 2)) -eq 0 ]; then
return 0 # true
else
return 1 # false
fi
}
# 测试布尔函数
for i in {1..5}; do
if is_even "$i"; then
echo "$i 是偶数"
else
echo "$i 是奇数"
fi
done
return 0表示成功,非0表示失败echo返回数据,使用return返回状态>&2;理解Shell函数中的变量作用域规则。
| 作用域 | 定义方式 | 可见范围 |
|---|---|---|
| 全局变量 | var=value |
整个脚本 |
| 局部变量 | local var=value |
当前函数 |
| 环境变量 | export var=value |
当前进程及子进程 |
| 只读变量 | readonly var=value |
整个脚本(不可修改) |
#!/bin/bash
# 变量作用域示例
echo "=== 全局变量和局部变量 ==="
# 全局变量
global_var="我是全局变量"
function test_scope {
# 局部变量
local local_var="我是局部变量"
echo "在函数内部:"
echo " 全局变量: $global_var"
echo " 局部变量: $local_var"
# 修改全局变量(没有local关键字)
global_var="在函数内修改的全局变量"
# 创建新的全局变量(不推荐)
new_global="在函数内创建的新全局变量"
}
echo "函数调用前:"
echo " 全局变量: $global_var"
echo " 局部变量: ${local_var:-未定义}"
echo " 新全局变量: ${new_global:-未定义}"
test_scope
echo -e "\n函数调用后:"
echo " 全局变量: $global_var"
echo " 局部变量: ${local_var:-未定义}"
echo " 新全局变量: ${new_global:-未定义}"
echo -e "\n=== 局部变量覆盖 ==="
var="外部变量"
function shadow_test {
local var="局部变量"
echo "在函数内: var = $var"
}
echo "变量覆盖测试:"
echo "函数外部: var = $var"
shadow_test
echo "函数外部: var = $var"
echo -e "\n=== 数组作用域 ==="
# 全局数组
global_array=("全局1" "全局2")
function array_scope {
# 局部数组
local local_array=("局部1" "局部2" "局部3")
echo "在函数内部:"
echo " 全局数组: ${global_array[@]}"
echo " 局部数组: ${local_array[@]}"
# 修改全局数组
global_array+=("在函数内添加")
}
echo "数组测试:"
array_scope
echo -e "\n在函数外部:"
echo " 全局数组: ${global_array[@]}"
echo " 局部数组: ${local_array[@]}"
echo -e "\n=== 环境变量 ==="
# 设置环境变量
export ENV_VAR="我是环境变量"
function check_env {
echo "在函数内环境变量: $ENV_VAR"
# 修改环境变量(只在当前进程有效)
export ENV_VAR="在函数内修改的环境变量"
}
echo "环境变量测试:"
echo "函数调用前: $ENV_VAR"
check_env
echo "函数调用后: $ENV_VAR"
echo -e "\n=== 子shell中的变量 ==="
function subshell_test {
local local_var="局部变量"
echo "在函数内: local_var = $local_var"
# 在子shell中修改变量
(
echo "在子shell中:"
echo " 修改前的 local_var: $local_var"
local_var="在子shell中修改"
echo " 修改后的 local_var: $local_var"
)
echo "回到函数内: local_var = $local_var"
}
echo "子shell测试:"
subshell_test
echo -e "\n=== 只读变量 ==="
readonly READONLY_VAR="只读变量"
function readonly_test {
echo "只读变量: $READONLY_VAR"
# 尝试修改只读变量(会报错)
# READONLY_VAR="尝试修改" # 取消注释会报错
}
readonly_test
echo -e "\n=== 变量引用 ==="
# 通过变量名引用另一个变量
name="Alice"
reference="name"
function reference_test {
local ref="$1"
echo "变量名: $ref"
echo "变量值: ${!ref}" # 间接引用
}
echo "变量引用测试:"
reference_test "reference"
echo -e "\n=== 动态变量名 ==="
function create_variables {
local prefix="$1"
local count="$2"
for i in $(seq 1 "$count"); do
local var_name="${prefix}_${i}"
declare -g "$var_name"="值$i" # 使用declare创建全局变量
done
}
echo "动态创建变量:"
create_variables "dynamic" 3
echo "dynamic_1 = $dynamic_1"
echo "dynamic_2 = $dynamic_2"
echo "dynamic_3 = $dynamic_3"
echo -e "\n=== 命名空间模拟 ==="
# 使用前缀模拟命名空间
function namespace_example {
local ns_prefix="mylib_"
# 在函数内定义"私有"变量
local "${ns_prefix}private_var"="私有数据"
# 定义"公有"变量(没有前缀)
local public_var="公有数据"
# 通过echo返回需要公开的数据
echo "$public_var"
}
echo "命名空间模拟:"
public_result=$(namespace_example)
echo "公有结果: $public_result"
echo "私有变量: ${mylib_private_var:-未定义}"
echo -e "\n=== 变量生命周期 ==="
function lifecycle_test {
local counter=0
# 使用子shell创建独立作用域
(
local inner_var="内部变量"
echo "在子shell内: inner_var = $inner_var"
)
# 子shell外的变量不受影响
echo "在函数内: inner_var = ${inner_var:-未定义}"
# 计数器示例
((counter++))
echo "计数器: $counter"
}
echo "变量生命周期测试:"
lifecycle_test
lifecycle_test
local关键字定义变量学习如何创建和使用递归函数。
#!/bin/bash
# 递归函数示例
echo "=== 阶乘计算 ==="
# 计算阶乘 n! = n * (n-1) * ... * 1
factorial() {
local n="$1"
# 基本情况
if [ "$n" -eq 0 ] || [ "$n" -eq 1 ]; then
echo 1
return
fi
# 递归情况
local prev=$(factorial $((n - 1)))
echo $((n * prev))
}
echo "阶乘测试:"
for i in {0..5}; do
result=$(factorial "$i")
echo "$i! = $result"
done
echo -e "\n=== 斐波那契数列 ==="
# 计算斐波那契数列
fibonacci() {
local n="$1"
# 基本情况
if [ "$n" -eq 0 ]; then
echo 0
elif [ "$n" -eq 1 ]; then
echo 1
else
# 递归情况
local fib1=$(fibonacci $((n - 1)))
local fib2=$(fibonacci $((n - 2)))
echo $((fib1 + fib2))
fi
}
echo "斐波那契数列:"
for i in {0..10}; do
result=$(fibonacci "$i")
echo "F($i) = $result"
done
echo -e "\n=== 目录树遍历 ==="
# 递归遍历目录
traverse_directory() {
local dir="$1"
local indent="$2"
# 基本情况:目录不存在
if [ ! -d "$dir" ]; then
return
fi
# 处理当前目录
echo "${indent}📁 $(basename "$dir")/"
# 递归处理子目录和文件
for item in "$dir"/*; do
if [ -d "$item" ]; then
# 递归处理子目录
traverse_directory "$item" "${indent} "
elif [ -f "$item" ]; then
# 处理文件
echo "${indent} 📄 $(basename "$item")"
fi
done
}
echo "目录遍历测试:"
mkdir -p test_dir/{sub1,sub2,sub3/sub4}
touch test_dir/file1.txt test_dir/sub1/file2.txt test_dir/sub3/sub4/file3.txt
traverse_directory "test_dir" ""
echo -e "\n=== 二分查找 ==="
# 递归二分查找
binary_search() {
local arr=("$@")
local search_value="${arr[-1]}"
unset 'arr[${#arr[@]}-1]' # 移除最后一个元素(搜索值)
local low=0
local high=$((${#arr[@]} - 1))
# 内部递归函数
_binary_search_recursive() {
local low="$1"
local high="$2"
local search_value="$3"
local arr=("${@:4}")
# 基本情况:搜索范围无效
if [ "$low" -gt "$high" ]; then
echo "-1" # 未找到
return
fi
local mid=$(( (low + high) / 2 ))
local mid_value="${arr[$mid]}"
# 基本情况:找到值
if [ "$mid_value" -eq "$search_value" ]; then
echo "$mid" # 返回索引
elif [ "$mid_value" -lt "$search_value" ]; then
# 在右半部分递归搜索
_binary_search_recursive $((mid + 1)) "$high" "$search_value" "${arr[@]}"
else
# 在左半部分递归搜索
_binary_search_recursive "$low" $((mid - 1)) "$search_value" "${arr[@]}"
fi
}
# 调用内部递归函数
_binary_search_recursive "$low" "$high" "$search_value" "${arr[@]}"
}
echo "二分查找测试:"
sorted_array=(1 3 5 7 9 11 13 15)
echo "数组: ${sorted_array[*]}"
for target in 7 2 15 20; do
index=$(binary_search "${sorted_array[@]}" "$target")
if [ "$index" -eq "-1" ]; then
echo "值 $target 未找到"
else
echo "值 $target 在索引 $index 处"
fi
done
echo -e "\n=== 汉诺塔问题 ==="
# 汉诺塔递归解决方案
tower_of_hanoi() {
local n="$1" # 盘子数量
local source="$2" # 源柱
local destination="$3" # 目标柱
local auxiliary="$4" # 辅助柱
local step=0
# 内部递归函数
_hanoi() {
local n="$1"
local source="$2"
local destination="$3"
local auxiliary="$4"
# 基本情况:只有一个盘子
if [ "$n" -eq 1 ]; then
((step++))
echo "步骤 $step: 将盘子从 $source 移动到 $destination"
return
fi
# 递归情况:
# 1. 将n-1个盘子从源柱移动到辅助柱
_hanoi $((n - 1)) "$source" "$auxiliary" "$destination"
# 2. 将最大的盘子从源柱移动到目标柱
((step++))
echo "步骤 $step: 将盘子从 $source 移动到 $destination"
# 3. 将n-1个盘子从辅助柱移动到目标柱
_hanoi $((n - 1)) "$auxiliary" "$destination" "$source"
}
echo "汉诺塔解决方案 (n=$n):"
_hanoi "$n" "$source" "$destination" "$auxiliary"
}
# 测试汉诺塔(小规模,避免过多输出)
tower_of_hanoi 3 "A" "C" "B"
echo -e "\n=== 递归深度控制 ==="
# 带深度控制的递归函数
limited_recursion() {
local current_depth="$1"
local max_depth="$2"
# 基本情况:达到最大深度
if [ "$current_depth" -ge "$max_depth" ]; then
echo "达到最大深度: $max_depth"
return
fi
echo "当前深度: $current_depth"
# 递归调用
limited_recursion $((current_depth + 1)) "$max_depth"
}
echo "递归深度控制测试:"
limited_recursion 0 5
echo -e "\n=== 尾递归优化 ==="
# 尾递归阶乘(使用累加器)
tail_recursive_factorial() {
local n="$1"
local accumulator="${2:-1}"
# 基本情况
if [ "$n" -eq 0 ] || [ "$n" -eq 1 ]; then
echo "$accumulator"
return
fi
# 尾递归调用
tail_recursive_factorial $((n - 1)) $((n * accumulator))
}
echo "尾递归阶乘测试:"
for i in {0..5}; do
result=$(tail_recursive_factorial "$i")
echo "$i! = $result"
done
echo -e "\n=== 递归文件搜索 ==="
# 递归搜索文件
find_files() {
local dir="$1"
local pattern="$2"
# 基本情况:目录不存在
if [ ! -d "$dir" ]; then
return
fi
# 搜索当前目录
for file in "$dir"/*; do
if [ -f "$file" ] && [[ "$(basename "$file")" == $pattern ]]; then
echo "找到: $file"
elif [ -d "$file" ]; then
# 递归搜索子目录
find_files "$file" "$pattern"
fi
done
}
echo "递归文件搜索测试:"
find_files "test_dir" "*.txt"
# 清理测试文件
rm -rf test_dir
通过实际脚本示例展示Shell函数的综合应用。
使用函数创建模块化的系统管理工具。
#!/bin/bash
# 系统管理工具包 - 函数综合示例
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 配置
LOG_FILE="/var/log/system_toolkit.log"
BACKUP_DIR="/backup"
MAX_LOG_SIZE="10M"
# 初始化系统
init_system() {
mkdir -p "$BACKUP_DIR"
mkdir -p "$(dirname "$LOG_FILE")"
echo "$(date): 系统工具包初始化" >> "$LOG_FILE"
}
# 日志功能
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
case $level in
"ERROR") echo -e "${RED}[ERROR]${NC} $message" ;;
"WARNING") echo -e "${YELLOW}[WARNING]${NC} $message" ;;
"INFO") echo -e "${BLUE}[INFO]${NC} $message" ;;
"SUCCESS") echo -e "${GREEN}[SUCCESS]${NC} $message" ;;
esac
}
# 备份管理功能
backup_system() {
local backup_name="$1"
local sources=("${@:2}")
if [ ${#sources[@]} -eq 0 ]; then
log_message "ERROR" "没有指定备份源"
return 1
fi
local timestamp=$(date '+%Y%m%d_%H%M%S')
local backup_path="$BACKUP_DIR/${backup_name}_${timestamp}.tar.gz"
log_message "INFO" "开始备份: $backup_name"
log_message "INFO" "备份源: ${sources[*]}"
# 创建备份
if tar -czf "$backup_path" "${sources[@]}" 2>/dev/null; then
local size=$(du -h "$backup_path" | cut -f1)
log_message "SUCCESS" "备份创建成功: $backup_path ($size)"
echo "$backup_path"
else
log_message "ERROR" "备份创建失败"
return 1
fi
}
# 系统监控功能
system_monitor() {
local check_type="$1"
case "$check_type" in
"cpu")
_check_cpu_usage
;;
"memory")
_check_memory_usage
;;
"disk")
_check_disk_usage
;;
"all")
_check_cpu_usage
_check_memory_usage
_check_disk_usage
;;
*)
log_message "ERROR" "未知的检查类型: $check_type"
return 1
;;
esac
}
_check_cpu_usage() {
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
cpu_usage=${cpu_usage%.*}
log_message "INFO" "CPU使用率: ${cpu_usage}%"
if [ "$cpu_usage" -gt 90 ]; then
log_message "WARNING" "CPU使用率过高"
fi
}
_check_memory_usage() {
local memory_info=$(free | grep Mem)
local total_memory=$(echo $memory_info | awk '{print $2}')
local used_memory=$(echo $memory_info | awk '{print $3}')
local memory_usage=$((used_memory * 100 / total_memory))
log_message "INFO" "内存使用率: ${memory_usage}%"
if [ "$memory_usage" -gt 90 ]; then
log_message "WARNING" "内存使用率过高"
fi
}
_check_disk_usage() {
local disk_info=$(df / | awk 'NR==2')
local total_disk=$(echo $disk_info | awk '{print $2}')
local used_disk=$(echo $disk_info | awk '{print $3}')
local disk_usage=$(echo $disk_info | awk '{print $5}' | sed 's/%//')
log_message "INFO" "磁盘使用率: ${disk_usage}%"
if [ "$disk_usage" -gt 90 ]; then
log_message "WARNING" "磁盘使用率过高"
fi
}
# 用户管理功能
user_manager() {
local action="$1"
local username="$2"
case "$action" in
"create")
_create_user "$username"
;;
"delete")
_delete_user "$username"
;;
"list")
_list_users
;;
*)
log_message "ERROR" "未知的用户操作: $action"
return 1
;;
esac
}
_create_user() {
local username="$1"
if [ -z "$username" ]; then
log_message "ERROR" "请提供用户名"
return 1
fi
if id "$username" &>/dev/null; then
log_message "WARNING" "用户已存在: $username"
return 1
fi
if useradd -m "$username" 2>/dev/null; then
log_message "SUCCESS" "用户创建成功: $username"
else
log_message "ERROR" "用户创建失败: $username"
return 1
fi
}
_delete_user() {
local username="$1"
if [ -z "$username" ]; then
log_message "ERROR" "请提供用户名"
return 1
fi
if ! id "$username" &>/dev/null; then
log_message "WARNING" "用户不存在: $username"
return 1
fi
if userdel -r "$username" 2>/dev/null; then
log_message "SUCCESS" "用户删除成功: $username"
else
log_message "ERROR" "用户删除失败: $username"
return 1
fi
}
_list_users() {
log_message "INFO" "系统用户列表:"
cut -d: -f1 /etc/passwd | sort | while read user; do
echo " - $user"
done
}
# 服务管理功能
service_manager() {
local service_name="$1"
local action="$2"
if [ -z "$service_name" ] || [ -z "$action" ]; then
log_message "ERROR" "请提供服务名和操作"
return 1
fi
case "$action" in
"start")
_service_action "$service_name" "start" "启动"
;;
"stop")
_service_action "$service_name" "stop" "停止"
;;
"restart")
_service_action "$service_name" "restart" "重启"
;;
"status")
_service_action "$service_name" "status" "状态"
;;
*)
log_message "ERROR" "未知的服务操作: $action"
return 1
;;
esac
}
_service_action() {
local service_name="$1"
local action="$2"
local action_name="$3"
if systemctl "$action" "$service_name" &>/dev/null; then
log_message "SUCCESS" "服务${action_name}成功: $service_name"
else
log_message "ERROR" "服务${action_name}失败: $service_name"
return 1
fi
}
# 网络诊断功能
network_diagnosis() {
local target="$1"
if [ -z "$target" ]; then
target="8.8.8.8"
fi
log_message "INFO" "开始网络诊断: $target"
# Ping测试
if ping -c 3 "$target" &>/dev/null; then
log_message "SUCCESS" "Ping测试成功: $target"
else
log_message "ERROR" "Ping测试失败: $target"
fi
# DNS测试
if nslookup "google.com" &>/dev/null; then
log_message "SUCCESS" "DNS解析正常"
else
log_message "ERROR" "DNS解析失败"
fi
# 端口测试
if nc -z "google.com" 80 &>/dev/null; then
log_message "SUCCESS" "HTTP端口(80)可达"
else
log_message "ERROR" "HTTP端口(80)不可达"
fi
}
# 日志管理功能
log_manager() {
local action="$1"
local log_file="${2:-$LOG_FILE}"
case "$action" in
"view")
_view_log "$log_file"
;;
"clean")
_clean_log "$log_file"
;;
"stats")
_log_stats "$log_file"
;;
*)
log_message "ERROR" "未知的日志操作: $action"
return 1
;;
esac
}
_view_log() {
local log_file="$1"
if [ ! -f "$log_file" ]; then
log_message "ERROR" "日志文件不存在: $log_file"
return 1
fi
echo "=== 日志内容 ==="
tail -20 "$log_file"
}
_clean_log() {
local log_file="$1"
if [ ! -f "$log_file" ]; then
log_message "ERROR" "日志文件不存在: $log_file"
return 1
fi
if : > "$log_file"; then
log_message "SUCCESS" "日志文件已清空: $log_file"
else
log_message "ERROR" "日志文件清空失败: $log_file"
return 1
fi
}
_log_stats() {
local log_file="$1"
if [ ! -f "$log_file" ]; then
log_message "ERROR" "日志文件不存在: $log_file"
return 1
fi
local total_lines=$(wc -l < "$log_file")
local error_count=$(grep -c "ERROR" "$log_file")
local warning_count=$(grep -c "WARNING" "$log_file")
local info_count=$(grep -c "INFO" "$log_file")
local success_count=$(grep -c "SUCCESS" "$log_file")
echo "=== 日志统计 ==="
echo "总行数: $total_lines"
echo "错误数: $error_count"
echo "警告数: $warning_count"
echo "信息数: $info_count"
echo "成功数: $success_count"
}
# 主菜单系统
main_menu() {
while true; do
echo -e "\n${BLUE}=== 系统管理工具包 ===${NC}"
echo "1. 系统监控"
echo "2. 备份管理"
echo "3. 用户管理"
echo "4. 服务管理"
echo "5. 网络诊断"
echo "6. 日志管理"
echo "7. 退出"
read -p "请选择操作 [1-7]: " choice
case $choice in
1)
system_monitor_menu
;;
2)
backup_menu
;;
3)
user_menu
;;
4)
service_menu
;;
5)
network_menu
;;
6)
log_menu
;;
7)
echo -e "${GREEN}感谢使用系统管理工具包,再见!${NC}"
break
;;
*)
echo -e "${RED}无效选择,请重新输入${NC}"
;;
esac
echo
read -p "按回车键继续..."
done
}
# 子菜单函数
system_monitor_menu() {
echo -e "\n${YELLOW}=== 系统监控 ===${NC}"
echo "1. 检查CPU使用率"
echo "2. 检查内存使用率"
echo "3. 检查磁盘使用率"
echo "4. 检查所有"
read -p "请选择: " sub_choice
case $sub_choice in
1) system_monitor "cpu" ;;
2) system_monitor "memory" ;;
3) system_monitor "disk" ;;
4) system_monitor "all" ;;
*) echo "无效选择" ;;
esac
}
backup_menu() {
echo -e "\n${YELLOW}=== 备份管理 ===${NC}"
read -p "请输入备份名称: " backup_name
read -p "请输入备份源(空格分隔): " -a sources
if backup_system "$backup_name" "${sources[@]}"; then
echo "备份创建成功"
else
echo "备份创建失败"
fi
}
user_menu() {
echo -e "\n${YELLOW}=== 用户管理 ===${NC}"
echo "1. 创建用户"
echo "2. 删除用户"
echo "3. 列出用户"
read -p "请选择: " sub_choice
case $sub_choice in
1)
read -p "请输入用户名: " username
user_manager "create" "$username"
;;
2)
read -p "请输入用户名: " username
user_manager "delete" "$username"
;;
3)
user_manager "list"
;;
*)
echo "无效选择"
;;
esac
}
service_menu() {
echo -e "\n${YELLOW}=== 服务管理 ===${NC}"
read -p "请输入服务名: " service_name
echo "1. 启动服务"
echo "2. 停止服务"
echo "3. 重启服务"
echo "4. 查看状态"
read -p "请选择操作: " sub_choice
case $sub_choice in
1) service_manager "$service_name" "start" ;;
2) service_manager "$service_name" "stop" ;;
3) service_manager "$service_name" "restart" ;;
4) service_manager "$service_name" "status" ;;
*) echo "无效选择" ;;
esac
}
network_menu() {
echo -e "\n${YELLOW}=== 网络诊断 ===${NC}"
read -p "请输入诊断目标(默认: 8.8.8.8): " target
network_diagnosis "$target"
}
log_menu() {
echo -e "\n${YELLOW}=== 日志管理 ===${NC}"
read -p "请输入日志文件(默认: $LOG_FILE): " log_file
echo "1. 查看日志"
echo "2. 清空日志"
echo "3. 日志统计"
read -p "请选择操作: " sub_choice
case $sub_choice in
1) log_manager "view" "$log_file" ;;
2) log_manager "clean" "$log_file" ;;
3) log_manager "stats" "$log_file" ;;
*) echo "无效选择" ;;
esac
}
# 命令行参数处理
handle_arguments() {
case "$1" in
"monitor")
system_monitor "${2:-all}"
;;
"backup")
backup_system "$2" "${@:3}"
;;
"user")
user_manager "$2" "$3"
;;
"service")
service_manager "$2" "$3"
;;
"network")
network_diagnosis "$2"
;;
"log")
log_manager "$2" "$3"
;;
"help"|"-h"|"--help")
show_help
;;
*)
echo "未知命令: $1"
show_help
return 1
;;
esac
}
show_help() {
cat << EOF
用法: $0 [命令] [参数...]
命令:
monitor [类型] 系统监控 (cpu|memory|disk|all)
backup [名称] [源...] 创建备份
user [操作] [用户] 用户管理 (create|delete|list)
service [服务] [操作] 服务管理 (start|stop|restart|status)
network [目标] 网络诊断
log [操作] [文件] 日志管理 (view|clean|stats)
help 显示此帮助信息
示例:
$0 monitor cpu
$0 backup home /home/user
$0 user create alice
$0 service nginx restart
$0 network 8.8.8.8
$0 log view
如果没有提供命令,将启动交互式菜单。
EOF
}
# 主函数
main() {
init_system
# 如果有命令行参数,直接处理
if [ $# -gt 0 ]; then
handle_arguments "$@"
else
# 否则显示交互式菜单
echo -e "${GREEN}系统管理工具包已启动${NC}"
log_message "INFO" "应用程序启动(交互模式)"
main_menu
log_message "INFO" "应用程序退出"
fi
}
# 脚本入口
main "$@"
./system_toolkit.sh - 启动交互式菜单./system_toolkit.sh monitor cpu - 检查CPU使用率./system_toolkit.sh backup home /home/user - 备份home目录./system_toolkit.sh user create alice - 创建用户alice./system_toolkit.sh help - 显示帮助信息
local关键字定义局部变量使用 set -x 开启调试模式查看函数执行过程。在函数关键位置添加 echo 语句输出调试信息。使用 declare -f function_name 查看函数定义。对于复杂函数,可以单独测试每个函数。