活动公告

系统通知
05-18 21:22
系统通知
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,资源失效请在帖子内回复要求补档,会尽快处理!
10-23 09:31

深入浅出Fedora自动化部署脚本编写从基础命令到高级应用全面解析

SunJu_FaceMall

3万

主题

2860

科技点

3万

积分

白金月票

碾压王

积分
32872

塔罗立华奏

<font color=白金月票" /> 发表于 2025-9-3 12:20:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

Fedora作为一款功能强大的Linux发行版,广泛应用于服务器环境和个人开发工作站。在大规模部署和管理Fedora系统时,手动配置不仅耗时耗力,而且容易出错。自动化部署脚本能够显著提高效率,减少人为错误,确保配置的一致性。本文将从基础命令开始,逐步深入到高级应用,全面解析Fedora自动化部署脚本的编写技巧。

基础命令篇

在编写自动化部署脚本之前,我们需要掌握一些基础的Linux命令。这些命令是构建复杂脚本的基础模块。

文件和目录操作
  1. # 创建目录
  2. mkdir /path/to/directory
  3. # 递归创建目录
  4. mkdir -p /path/to/nested/directory
  5. # 复制文件或目录
  6. cp source_file destination_file
  7. cp -r source_directory destination_directory
  8. # 移动/重命名文件或目录
  9. mv old_name new_name
  10. # 删除文件
  11. rm file_name
  12. # 递归删除目录及其内容
  13. rm -r directory_name
  14. # 强制删除,不提示确认
  15. rm -f file_name
  16. rm -rf directory_name
  17. # 创建空文件或更新文件时间戳
  18. touch file_name
  19. # 查看文件内容
  20. cat file_name
  21. # 分页查看文件内容
  22. less file_name
  23. more file_name
  24. # 查看文件头部内容
  25. head -n 10 file_name
  26. # 查看文件尾部内容
  27. tail -n 10 file_name
  28. tail -f file_name  # 实时查看文件更新
复制代码

文本处理
  1. # 搜索文本
  2. grep "pattern" file_name
  3. # 递归搜索目录中的文件
  4. grep -r "pattern" directory_name
  5. # 替换文本
  6. sed 's/old_text/new_text/g' file_name
  7. # 排序
  8. sort file_name
  9. # 去重
  10. sort file_name | uniq
  11. # 统计行数、单词数和字符数
  12. wc file_name
  13. # 提取列
  14. cut -d',' -f1,3 file_name  # 使用逗号作为分隔符,提取第1和第3列
复制代码

权限管理
  1. # 修改文件权限
  2. chmod 755 file_name  # rwxr-xr-x
  3. chmod u+x file_name  # 给文件所有者添加执行权限
  4. # 修改文件所有者
  5. chown user:group file_name
  6. # 修改文件所属组
  7. chgrp group file_name
复制代码

软件包管理(DNF)
  1. # 安装软件包
  2. dnf install package_name
  3. # 卸载软件包
  4. dnf remove package_name
  5. # 更新所有软件包
  6. dnf update
  7. # 搜索软件包
  8. dnf search keyword
  9. # 显示软件包信息
  10. dnf info package_name
  11. # 列出已安装的软件包
  12. dnf list installed
  13. # 清理缓存
  14. dnf clean all
复制代码

系统服务管理
  1. # 启动服务
  2. systemctl start service_name
  3. # 停止服务
  4. systemctl stop service_name
  5. # 重启服务
  6. systemctl restart service_name
  7. # 重新加载服务配置
  8. systemctl reload service_name
  9. # 启用服务开机自启
  10. systemctl enable service_name
  11. # 禁用服务开机自启
  12. systemctl disable service_name
  13. # 查看服务状态
  14. systemctl status service_name
  15. # 查看所有服务状态
  16. systemctl list-units --type=service
复制代码

网络配置
  1. # 显示网络接口信息
  2. ip addr show
  3. # 显示路由表
  4. ip route show
  5. # 测试网络连通性
  6. ping host_name_or_ip
  7. # 显示网络连接
  8. ss -tuln
  9. # 下载文件
  10. wget URL
  11. curl -O URL
  12. # 安全复制文件
  13. scp user@remote_host:/path/to/remote/file /path/to/local/directory
复制代码

Shell脚本基础

掌握了基础命令后,我们可以开始学习如何将这些命令组合成Shell脚本。

脚本的基本结构

一个基本的Shell脚本包含以下元素:
  1. #!/bin/bash
  2. # 脚本说明:这是一个简单的示例脚本
  3. # 作者:Your Name
  4. # 日期:YYYY-MM-DD
  5. # 定义变量
  6. GREETING="Hello, World!"
  7. # 主逻辑
  8. echo $GREETING
  9. # 退出脚本
  10. exit 0
复制代码

• #!/bin/bash:称为shebang,告诉系统使用Bash解释器执行此脚本
• 注释:以#开头的行是注释,用于解释代码
• 变量:用于存储数据,使用=赋值,引用时使用$符号
• exit 0:表示脚本成功执行并退出

变量和参数
  1. #!/bin/bash
  2. # 定义变量
  3. NAME="Fedora"
  4. VERSION="35"
  5. # 使用变量
  6. echo "Operating System: $NAME"
  7. echo "Version: $VERSION"
  8. # 特殊变量
  9. echo "Script name: $0"          # 脚本名称
  10. echo "First parameter: $1"      # 第一个参数
  11. echo "Second parameter: $2"     # 第二个参数
  12. echo "All parameters: $@"      # 所有参数
  13. echo "Number of parameters: $#" # 参数个数
  14. echo "Process ID: $$"           # 当前进程ID
  15. # 命令替换
  16. CURRENT_DIR=$(pwd)
  17. echo "Current directory: $CURRENT_DIR"
  18. # 算术运算
  19. SUM=$((5 + 3))
  20. echo "5 + 3 = $SUM"
复制代码

用户输入
  1. #!/bin/bash
  2. # 读取用户输入
  3. echo "Enter your name:"
  4. read NAME
  5. echo "Hello, $NAME!"
  6. # 带提示的读取
  7. read -p "Enter your age: " AGE
  8. echo "You are $AGE years old."
  9. # 密码输入(不显示)
  10. read -s -p "Enter your password: " PASSWORD
  11. echo -e "\nPassword received."
  12. # 选择菜单
  13. echo "Select an option:"
  14. echo "1) Option 1"
  15. echo "2) Option 2"
  16. echo "3) Option 3"
  17. read -p "Enter your choice [1-3]: " CHOICE
  18. case $CHOICE in
  19.     1)
  20.         echo "You selected Option 1"
  21.         ;;
  22.     2)
  23.         echo "You selected Option 2"
  24.         ;;
  25.     3)
  26.         echo "You selected Option 3"
  27.         ;;
  28.     *)
  29.         echo "Invalid option"
  30.         ;;
  31. esac
复制代码

中级应用

掌握了基础之后,我们可以学习更复杂的脚本编写技巧。

条件判断
  1. #!/bin/bash
  2. # 文件测试
  3. FILE="/etc/fedora-release"
  4. if [ -f "$FILE" ]; then
  5.     echo "$FILE exists and is a regular file."
  6. else
  7.     echo "$FILE does not exist or is not a regular file."
  8. fi
  9. # 数字比较
  10. NUM1=10
  11. NUM2=20
  12. if [ $NUM1 -eq $NUM2 ]; then
  13.     echo "$NUM1 is equal to $NUM2"
  14. elif [ $NUM1 -lt $NUM2 ]; then
  15.     echo "$NUM1 is less than $NUM2"
  16. else
  17.     echo "$NUM1 is greater than $NUM2"
  18. fi
  19. # 字符串比较
  20. STRING1="Fedora"
  21. STRING2="fedora"
  22. if [ "$STRING1" = "$STRING2" ]; then
  23.     echo "Strings are equal"
  24. else
  25.     echo "Strings are not equal"
  26. fi
  27. # 逻辑运算
  28. AGE=25
  29. if [ $AGE -gt 18 ] && [ $AGE -lt 65 ]; then
  30.     echo "You are an adult"
  31. fi
  32. if [ $AGE -le 18 ] || [ $AGE -ge 65 ]; then
  33.     echo "You are not in the typical working age"
  34. fi
  35. # 测试命令是否存在
  36. if command -v dnf >/dev/null 2>&1; then
  37.     echo "DNF package manager is available"
  38. else
  39.     echo "DNF package manager is not available"
  40. fi
复制代码

循环结构
  1. #!/bin/bash
  2. # for循环 - 遍历列表
  3. echo "Fruits:"
  4. for FRUIT in apple banana cherry; do
  5.     echo "  $FRUIT"
  6. done
  7. # for循环 - 数字范围
  8. echo "Counting to 5:"
  9. for i in {1..5}; do
  10.     echo "  $i"
  11. done
  12. # C风格for循环
  13. echo "Counting down from 10:"
  14. for ((i=10; i>=0; i--)); do
  15.     echo "  $i"
  16. done
  17. # while循环
  18. COUNT=0
  19. echo "Counting to 5 with while loop:"
  20. while [ $COUNT -le 5 ]; do
  21.     echo "  $COUNT"
  22.     COUNT=$((COUNT + 1))
  23. done
  24. # until循环
  25. COUNT=0
  26. echo "Counting to 5 with until loop:"
  27. until [ $COUNT -gt 5 ]; do
  28.     echo "  $COUNT"
  29.     COUNT=$((COUNT + 1))
  30. done
  31. # break和continue
  32. echo "Even numbers up to 10:"
  33. for i in {1..10}; do
  34.     if [ $((i % 2)) -ne 0 ]; then
  35.         continue  # 跳过奇数
  36.     fi
  37.    
  38.     echo "  $i"
  39.    
  40.     if [ $i -eq 8 ]; then
  41.         break  # 在8处停止
  42.     fi
  43. done
  44. # 遍历文件
  45. echo "Files in current directory:"
  46. for FILE in *; do
  47.     if [ -f "$FILE" ]; then
  48.         echo "  $FILE"
  49.     fi
  50. done
  51. # 读取文件行
  52. echo "Reading /etc/os-release:"
  53. while IFS= read -r LINE; do
  54.     echo "  $LINE"
  55. done < /etc/os-release
复制代码

函数
  1. #!/bin/bash
  2. # 定义简单函数
  3. hello() {
  4.     echo "Hello, World!"
  5. }
  6. # 调用函数
  7. hello
  8. # 带参数的函数
  9. greet() {
  10.     echo "Hello, $1!"
  11. }
  12. greet "Fedora User"
  13. # 带返回值的函数
  14. add() {
  15.     local SUM=$(( $1 + $2 ))
  16.     echo $SUM
  17. }
  18. RESULT=$(add 10 20)
  19. echo "10 + 20 = $RESULT"
  20. # 使用return返回状态码
  21. check_file() {
  22.     if [ -f "$1" ]; then
  23.         return 0  # 成功
  24.     else
  25.         return 1  # 失败
  26.     fi
  27. }
  28. check_file "/etc/fedora-release"
  29. if [ $? -eq 0 ]; then
  30.     echo "File exists"
  31. else
  32.     echo "File does not exist"
  33. fi
  34. # 局部变量和全局变量
  35. GLOBAL_VAR="I am global"
  36. demo_scope() {
  37.     local LOCAL_VAR="I am local"
  38.     GLOBAL_VAR="Modified global"
  39.     echo "Inside function:"
  40.     echo "  Global: $GLOBAL_VAR"
  41.     echo "  Local: $LOCAL_VAR"
  42. }
  43. demo_scope
  44. echo "Outside function:"
  45. echo "  Global: $GLOBAL_VAR"
  46. echo "  Local: $LOCAL_VAR"  # 这将是空的
  47. # 递归函数
  48. factorial() {
  49.     if [ $1 -le 1 ]; then
  50.         echo 1
  51.     else
  52.         local TEMP=$(factorial $(( $1 - 1 )))
  53.         echo $(( $1 * TEMP ))
  54.     fi
  55. }
  56. echo "Factorial of 5 is $(factorial 5)"
复制代码

数组操作
  1. #!/bin/bash
  2. # 定义数组
  3. DISTROS=("Fedora" "Ubuntu" "Debian" "CentOS" "Arch")
  4. # 访问数组元素
  5. echo "First distro: ${DISTROS[0]}"
  6. echo "Second distro: ${DISTROS[1]}"
  7. # 访问所有元素
  8. echo "All distros: ${DISTROS[@]}"
  9. echo "All distros: ${DISTROS[*]}"
  10. # 获取数组长度
  11. echo "Number of distros: ${#DISTROS[@]}"
  12. # 遍历数组
  13. echo "List of distros:"
  14. for DISTRO in "${DISTROS[@]}"; do
  15.     echo "  $DISTRO"
  16. done
  17. # 添加元素到数组
  18. DISTROS+=("openSUSE")
  19. echo "After adding openSUSE: ${DISTROS[@]}"
  20. # 从数组中删除元素
  21. unset DISTROS[2]  # 删除Debian
  22. echo "After removing Debian: ${DISTROS[@]}"
  23. # 数组切片
  24. echo "First three distros: ${DISTROS[@]:0:3}"
  25. # 关联数组(类似字典)
  26. declare -A PACKAGE_MANAGER
  27. PACKAGE_MANAGER["Fedora"]="dnf"
  28. PACKAGE_MANAGER["Ubuntu"]="apt"
  29. PACKAGE_MANAGER["Arch"]="pacman"
  30. echo "Package manager for Fedora: ${PACKAGE_MANAGER["Fedora"]}"
  31. echo "Package manager for Ubuntu: ${PACKAGE_MANAGER["Ubuntu"]}"
  32. # 遍历关联数组
  33. echo "Package managers:"
  34. for DISTRO in "${!PACKAGE_MANAGER[@]}"; do
  35.     echo "  $DISTRO: ${PACKAGE_MANAGER[$DISTRO]}"
  36. done
复制代码

高级应用

现在我们进入更高级的主题,这些技巧将使你的脚本更加健壮和专业。

错误处理
  1. #!/bin/bash
  2. # 使用set命令增强错误处理
  3. set -e  # 任何命令返回非零状态时立即退出
  4. set -u  # 使用未定义的变量时报错
  5. set -o pipefail  # 管道中任何命令失败时,整个管道失败
  6. # 自定义错误处理
  7. error_handler() {
  8.     echo "Error occurred in script at line: $1"
  9.     echo "Command exited with status: $2"
  10.     exit $2
  11. }
  12. # 使用trap捕获错误
  13. trap 'error_handler $LINENO $?' ERR
  14. # 示例:尝试创建目录
  15. echo "Creating directory..."
  16. mkdir -p /tmp/test_dir || {
  17.     echo "Failed to create directory"
  18.     exit 1
  19. }
  20. # 检查命令是否存在
  21. command_exists() {
  22.     command -v "$1" >/dev/null 2>&1
  23. }
  24. if ! command_exists "dnf"; then
  25.     echo "DNF package manager is not installed"
  26.     exit 1
  27. fi
  28. # 安全地执行命令
  29. safe_execute() {
  30.     echo "Executing: $*"
  31.     if "$@"; then
  32.         echo "Command executed successfully"
  33.     else
  34.         echo "Command failed with exit code: $?"
  35.         return 1
  36.     fi
  37. }
  38. # 使用安全执行
  39. safe_execute dnf check-update || echo "Update check failed, but continuing..."
  40. # 临时禁用错误处理
  41. echo "This command might fail, but we'll continue anyway"
  42. set +e
  43. command_that_might_fail
  44. set -e
  45. # 自定义错误消息
  46. die() {
  47.     echo "ERROR: $*" >&2
  48.     exit 1
  49. }
  50. # 使用自定义错误消息
  51. [ -f "/etc/fedora-release" ] || die "Fedora release file not found. This script requires Fedora."
  52. echo "Script completed successfully"
复制代码

日志记录
  1. #!/bin/bash
  2. # 配置日志
  3. LOG_FILE="/var/log/fedora_deploy.log"
  4. MAX_LOG_SIZE=1048576  # 1MB
  5. # 日志函数
  6. log() {
  7.     local LEVEL=$1
  8.     shift
  9.     local MESSAGE="$*"
  10.     local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
  11.    
  12.     echo "[$TIMESTAMP] [$LEVEL] $MESSAGE" | tee -a "$LOG_FILE"
  13. }
  14. info() {
  15.     log "INFO" "$@"
  16. }
  17. warn() {
  18.     log "WARN" "$@"
  19. }
  20. error() {
  21.     log "ERROR" "$@"
  22. }
  23. debug() {
  24.     if [ "$DEBUG" = "true" ]; then
  25.         log "DEBUG" "$@"
  26.     fi
  27. }
  28. # 检查并轮转日志
  29. rotate_log() {
  30.     if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE") -gt $MAX_LOG_SIZE ]; then
  31.         mv "$LOG_FILE" "${LOG_FILE}.old"
  32.         touch "$LOG_FILE"
  33.         info "Log file rotated"
  34.     fi
  35. }
  36. # 初始化日志
  37. init_logging() {
  38.     # 确保日志目录存在
  39.     mkdir -p "$(dirname "$LOG_FILE")"
  40.    
  41.     # 检查日志大小并轮转
  42.     rotate_log
  43.    
  44.     # 记录脚本启动
  45.     info "===== Script started ====="
  46.     info "Running as user: $(whoami)"
  47.     info "Script PID: $$"
  48. }
  49. # 示例使用
  50. init_logging
  51. info "Starting Fedora deployment"
  52. debug "Debug information: DNF version is $(dnf --version | head -1)"
  53. # 模拟一个操作
  54. if dnf check-update; then
  55.     info "Package check completed successfully"
  56. else
  57.     error "Package check failed"
  58. fi
  59. warn "This is a warning message"
  60. info "Script completed"
复制代码

配置管理
  1. #!/bin/bash
  2. # 配置文件路径
  3. CONFIG_FILE="/etc/fedora_deploy.conf"
  4. # 默认配置
  5. DEFAULT_CONFIG=(
  6.     "INSTALL_GUI=false"
  7.     "INSTALL_DEV_TOOLS=true"
  8.     "SETUP_FIREWALL=true"
  9.     "CREATE_USER=true"
  10.     "USER_NAME=fedorauser"
  11.     "ENABLE_SSH=true"
  12.     "SSH_PORT=22"
  13. )
  14. # 加载配置文件
  15. load_config() {
  16.     # 如果配置文件不存在,创建默认配置文件
  17.     if [ ! -f "$CONFIG_FILE" ]; then
  18.         info "Configuration file not found. Creating default configuration."
  19.         create_default_config
  20.     fi
  21.    
  22.     # 加载配置文件
  23.     source "$CONFIG_FILE"
  24.     info "Configuration loaded from $CONFIG_FILE"
  25. }
  26. # 创建默认配置文件
  27. create_default_config() {
  28.     {
  29.         echo "# Fedora Deployment Configuration"
  30.         echo "# Generated on $(date)"
  31.         echo ""
  32.         
  33.         for setting in "${DEFAULT_CONFIG[@]}"; do
  34.             echo "$setting"
  35.         done
  36.     } > "$CONFIG_FILE"
  37.    
  38.     # 设置适当的权限
  39.     chmod 644 "$CONFIG_FILE"
  40. }
  41. # 验证配置
  42. validate_config() {
  43.     local errors=0
  44.    
  45.     # 检查必需的配置项
  46.     if [ -z "$USER_NAME" ]; then
  47.         error "USER_NAME is not configured"
  48.         errors=$((errors + 1))
  49.     fi
  50.    
  51.     # 验证SSH端口
  52.     if ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || [ "$SSH_PORT" -lt 1 ] || [ "$SSH_PORT" -gt 65535 ]; then
  53.         error "Invalid SSH_PORT: $SSH_PORT"
  54.         errors=$((errors + 1))
  55.     fi
  56.    
  57.     # 验证布尔值
  58.     for var in INSTALL_GUI INSTALL_DEV_TOOLS SETUP_FIREWALL CREATE_USER ENABLE_SSH; do
  59.         if [ "${!var}" != "true" ] && [ "${!var}" != "false" ]; then
  60.             error "Invalid value for $var: ${!var}. Must be 'true' or 'false'."
  61.             errors=$((errors + 1))
  62.         fi
  63.     done
  64.    
  65.     if [ $errors -gt 0 ]; then
  66.         error "Configuration validation failed with $errors errors"
  67.         exit 1
  68.     fi
  69.    
  70.     info "Configuration validation passed"
  71. }
  72. # 显示当前配置
  73. show_config() {
  74.     echo "Current configuration:"
  75.     echo "  INSTALL_GUI: $INSTALL_GUI"
  76.     echo "  INSTALL_DEV_TOOLS: $INSTALL_DEV_TOOLS"
  77.     echo "  SETUP_FIREWALL: $SETUP_FIREWALL"
  78.     echo "  CREATE_USER: $CREATE_USER"
  79.     echo "  USER_NAME: $USER_NAME"
  80.     echo "  ENABLE_SSH: $ENABLE_SSH"
  81.     echo "  SSH_PORT: $SSH_PORT"
  82. }
  83. # 更新配置项
  84. update_config() {
  85.     local key=$1
  86.     local value=$2
  87.    
  88.     # 检查配置文件是否存在
  89.     if [ ! -f "$CONFIG_FILE" ]; then
  90.         error "Configuration file does not exist"
  91.         return 1
  92.     fi
  93.    
  94.     # 临时文件
  95.     local temp_config=$(mktemp)
  96.    
  97.     # 更新配置项
  98.     if grep -q "^$key=" "$CONFIG_FILE"; then
  99.         sed "s/^$key=.*/$key=$value/" "$CONFIG_FILE" > "$temp_config"
  100.     else
  101.         cp "$CONFIG_FILE" "$temp_config"
  102.         echo "$key=$value" >> "$temp_config"
  103.     fi
  104.    
  105.     # 替换原文件
  106.     mv "$temp_config" "$CONFIG_FILE"
  107.    
  108.     info "Configuration updated: $key=$value"
  109. }
  110. # 示例使用
  111. load_config
  112. validate_config
  113. show_config
  114. # 更新配置
  115. update_config "INSTALL_GUI" "true"
  116. # 重新加载配置
  117. load_config
  118. show_config
复制代码

信号处理
  1. #!/bin/bash
  2. # 信号处理函数
  3. cleanup() {
  4.     echo "Caught signal, cleaning up..."
  5.    
  6.     # 在这里执行清理操作
  7.     if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
  8.         echo "Removing temporary directory: $TEMP_DIR"
  9.         rm -rf "$TEMP_DIR"
  10.     fi
  11.    
  12.     # 停止后台进程
  13.     if [ -n "$BG_PID" ]; then
  14.         echo "Stopping background process (PID: $BG_PID)"
  15.         kill $BG_PID 2>/dev/null
  16.     fi
  17.    
  18.     echo "Cleanup completed"
  19.     exit 1
  20. }
  21. # 设置信号处理
  22. trap cleanup SIGINT SIGTERM
  23. # 创建临时目录
  24. TEMP_DIR=$(mktemp -d)
  25. echo "Created temporary directory: $TEMP_DIR"
  26. # 模拟长时间运行的任务
  27. long_running_task() {
  28.     echo "Starting long running task..."
  29.    
  30.     # 模拟工作
  31.     for i in {1..30}; do
  32.         echo "Working... $i/30"
  33.         sleep 1
  34.     done
  35.    
  36.     echo "Long running task completed"
  37. }
  38. # 在后台运行任务
  39. long_running_task &
  40. BG_PID=$!
  41. # 等待后台任务完成
  42. wait $BG_PID
  43. # 如果脚本正常完成,也进行清理
  44. echo "Script completed normally"
  45. cleanup
  46. exit 0
复制代码

实际案例:Fedora自动化部署脚本

现在,让我们将前面学到的知识组合起来,创建一个完整的Fedora自动化部署脚本。
  1. #!/bin/bash
  2. # Fedora 自动化部署脚本
  3. # 作者: System Admin
  4. # 日期: $(date +%Y-%m-%d)
  5. # 设置严格模式
  6. set -euo pipefail
  7. # 配置
  8. CONFIG_FILE="/etc/fedora_deploy.conf"
  9. LOG_FILE="/var/log/fedora_deploy.log"
  10. MAX_LOG_SIZE=1048576  # 1MB
  11. # 默认配置
  12. DEFAULT_CONFIG=(
  13.     "INSTALL_GUI=false"
  14.     "INSTALL_DEV_TOOLS=true"
  15.     "SETUP_FIREWALL=true"
  16.     "CREATE_USER=true"
  17.     "USER_NAME=fedorauser"
  18.     "ENABLE_SSH=true"
  19.     "SSH_PORT=22"
  20. )
  21. # 全局变量
  22. TEMP_DIR=""
  23. BG_PID=""
  24. # =============================================================================
  25. # 函数定义
  26. # =============================================================================
  27. # 日志函数
  28. log() {
  29.     local LEVEL=$1
  30.     shift
  31.     local MESSAGE="$*"
  32.     local TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
  33.    
  34.     echo "[$TIMESTAMP] [$LEVEL] $MESSAGE" | tee -a "$LOG_FILE"
  35. }
  36. info() {
  37.     log "INFO" "$@"
  38. }
  39. warn() {
  40.     log "WARN" "$@"
  41. }
  42. error() {
  43.     log "ERROR" "$@"
  44. }
  45. debug() {
  46.     if [ "${DEBUG:-false}" = "true" ]; then
  47.         log "DEBUG" "$@"
  48.     fi
  49. }
  50. # 错误处理
  51. error_handler() {
  52.     local LINE_NO=$1
  53.     local EXIT_CODE=$2
  54.    
  55.     error "Script failed at line $LINE_NO with exit code $EXIT_CODE"
  56.     cleanup
  57.     exit $EXIT_CODE
  58. }
  59. # 设置错误处理
  60. trap 'error_handler $LINENO $?' ERR
  61. # 清理函数
  62. cleanup() {
  63.     debug "Starting cleanup"
  64.    
  65.     # 清理临时目录
  66.     if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then
  67.         debug "Removing temporary directory: $TEMP_DIR"
  68.         rm -rf "$TEMP_DIR"
  69.     fi
  70.    
  71.     # 停止后台进程
  72.     if [ -n "$BG_PID" ]; then
  73.         debug "Stopping background process (PID: $BG_PID)"
  74.         kill $BG_PID 2>/dev/null || true
  75.     fi
  76.    
  77.     debug "Cleanup completed"
  78. }
  79. # 设置信号处理
  80. trap cleanup SIGINT SIGTERM
  81. # 检查并轮转日志
  82. rotate_log() {
  83.     if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) -gt $MAX_LOG_SIZE ]; then
  84.         mv "$LOG_FILE" "${LOG_FILE}.old"
  85.         touch "$LOG_FILE"
  86.         info "Log file rotated"
  87.     fi
  88. }
  89. # 创建默认配置文件
  90. create_default_config() {
  91.     info "Creating default configuration file"
  92.    
  93.     mkdir -p "$(dirname "$CONFIG_FILE")"
  94.    
  95.     {
  96.         echo "# Fedora Deployment Configuration"
  97.         echo "# Generated on $(date)"
  98.         echo ""
  99.         
  100.         for setting in "${DEFAULT_CONFIG[@]}"; do
  101.             echo "$setting"
  102.         done
  103.     } > "$CONFIG_FILE"
  104.    
  105.     chmod 644 "$CONFIG_FILE"
  106. }
  107. # 加载配置
  108. load_config() {
  109.     if [ ! -f "$CONFIG_FILE" ]; then
  110.         info "Configuration file not found. Creating default configuration."
  111.         create_default_config
  112.     fi
  113.    
  114.     source "$CONFIG_FILE"
  115.     info "Configuration loaded from $CONFIG_FILE"
  116. }
  117. # 验证配置
  118. validate_config() {
  119.     info "Validating configuration"
  120.     local errors=0
  121.    
  122.     # 检查必需的配置项
  123.     if [ -z "$USER_NAME" ]; then
  124.         error "USER_NAME is not configured"
  125.         errors=$((errors + 1))
  126.     fi
  127.    
  128.     # 验证SSH端口
  129.     if ! [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || [ "$SSH_PORT" -lt 1 ] || [ "$SSH_PORT" -gt 65535 ]; then
  130.         error "Invalid SSH_PORT: $SSH_PORT"
  131.         errors=$((errors + 1))
  132.     fi
  133.    
  134.     # 验证布尔值
  135.     for var in INSTALL_GUI INSTALL_DEV_TOOLS SETUP_FIREWALL CREATE_USER ENABLE_SSH; do
  136.         if [ "${!var}" != "true" ] && [ "${!var}" != "false" ]; then
  137.             error "Invalid value for $var: ${!var}. Must be 'true' or 'false'."
  138.             errors=$((errors + 1))
  139.         fi
  140.     done
  141.    
  142.     if [ $errors -gt 0 ]; then
  143.         error "Configuration validation failed with $errors errors"
  144.         exit 1
  145.     fi
  146.    
  147.     info "Configuration validation passed"
  148. }
  149. # 检查是否以root运行
  150. check_root() {
  151.     if [ "$(id -u)" -ne 0 ]; then
  152.         error "This script must be run as root"
  153.         exit 1
  154.     fi
  155.    
  156.     info "Running as root"
  157. }
  158. # 更新系统
  159. update_system() {
  160.     info "Updating system packages"
  161.    
  162.     # 更新DNF缓存
  163.     dnf makecache || warn "Failed to update DNF cache"
  164.    
  165.     # 系统更新
  166.     dnf update -y || {
  167.         error "Failed to update system packages"
  168.         return 1
  169.     }
  170.    
  171.     info "System updated successfully"
  172. }
  173. # 安装基础软件包
  174. install_base_packages() {
  175.     info "Installing base packages"
  176.    
  177.     local BASE_PACKAGES=(
  178.         "vim"
  179.         "wget"
  180.         "curl"
  181.         "git"
  182.         "tar"
  183.         "gzip"
  184.         "unzip"
  185.         "htop"
  186.         "ncdu"
  187.         "tmux"
  188.         "nmap"
  189.         "tcpdump"
  190.         "bind-utils"
  191.         "net-tools"
  192.     )
  193.    
  194.     for package in "${BASE_PACKAGES[@]}"; do
  195.         info "Installing $package"
  196.         dnf install -y "$package" || warn "Failed to install $package"
  197.     done
  198.    
  199.     info "Base packages installation completed"
  200. }
  201. # 安装GUI(如果配置为true)
  202. install_gui() {
  203.     if [ "$INSTALL_GUI" != "true" ]; then
  204.         info "GUI installation skipped (disabled in configuration)"
  205.         return 0
  206.     fi
  207.    
  208.     info "Installing GUI packages"
  209.    
  210.     local GUI_PACKAGES=(
  211.         "@graphical-environment"
  212.         "@base-x"
  213.         "gnome-shell"
  214.         "gnome-terminal"
  215.         "nautilus"
  216.     )
  217.    
  218.     for package in "${GUI_PACKAGES[@]}"; do
  219.         info "Installing $package"
  220.         dnf install -y "$package" || warn "Failed to install $package"
  221.     done
  222.    
  223.     # 设置默认目标为图形界面
  224.     systemctl set-default graphical.target || warn "Failed to set default target to graphical"
  225.    
  226.     info "GUI installation completed"
  227. }
  228. # 安装开发工具(如果配置为true)
  229. install_dev_tools() {
  230.     if [ "$INSTALL_DEV_TOOLS" != "true" ]; then
  231.         info "Development tools installation skipped (disabled in configuration)"
  232.         return 0
  233.     fi
  234.    
  235.     info "Installing development tools"
  236.    
  237.     local DEV_PACKAGES=(
  238.         "@development-tools"
  239.         "gcc"
  240.         "gcc-c++"
  241.         "make"
  242.         "cmake"
  243.         "python3"
  244.         "python3-pip"
  245.         "nodejs"
  246.         "npm"
  247.         "golang"
  248.         "docker"
  249.         "postgresql"
  250.         "postgresql-server"
  251.         "redis"
  252.     )
  253.    
  254.     for package in "${DEV_PACKAGES[@]}"; do
  255.         info "Installing $package"
  256.         dnf install -y "$package" || warn "Failed to install $package"
  257.     done
  258.    
  259.     # 启用并启动Docker服务
  260.     if command -v docker >/dev/null 2>&1; then
  261.         systemctl enable --now docker || warn "Failed to enable and start Docker"
  262.     fi
  263.    
  264.     info "Development tools installation completed"
  265. }
  266. # 设置防火墙(如果配置为true)
  267. setup_firewall() {
  268.     if [ "$SETUP_FIREWALL" != "true" ]; then
  269.         info "Firewall setup skipped (disabled in configuration)"
  270.         return 0
  271.     fi
  272.    
  273.     info "Setting up firewall"
  274.    
  275.     # 安装firewalld
  276.     dnf install -y firewalld || {
  277.         error "Failed to install firewalld"
  278.         return 1
  279.     }
  280.    
  281.     # 启用并启动firewalld
  282.     systemctl enable --now firewalld || {
  283.         error "Failed to enable and start firewalld"
  284.         return 1
  285.     }
  286.    
  287.     # 如果启用了SSH,打开SSH端口
  288.     if [ "$ENABLE_SSH" = "true" ]; then
  289.         firewall-cmd --permanent --add-port="${SSH_PORT}/tcp" || warn "Failed to open SSH port in firewall"
  290.         firewall-cmd --reload || warn "Failed to reload firewall rules"
  291.     fi
  292.    
  293.     info "Firewall setup completed"
  294. }
  295. # 创建用户(如果配置为true)
  296. create_user() {
  297.     if [ "$CREATE_USER" != "true" ]; then
  298.         info "User creation skipped (disabled in configuration)"
  299.         return 0
  300.     fi
  301.    
  302.     info "Creating user: $USER_NAME"
  303.    
  304.     # 检查用户是否已存在
  305.     if id "$USER_NAME" &>/dev/null; then
  306.         warn "User $USER_NAME already exists"
  307.         return 0
  308.     fi
  309.    
  310.     # 创建用户
  311.     useradd -m -s /bin/bash "$USER_NAME" || {
  312.         error "Failed to create user $USER_NAME"
  313.         return 1
  314.     }
  315.    
  316.     # 设置密码(这里使用随机密码,实际应用中可能需要其他方式)
  317.     local PASSWORD=$(openssl rand -base64 12)
  318.     echo "$USER_NAME:$PASSWORD" | chpasswd || {
  319.         error "Failed to set password for user $USER_NAME"
  320.         return 1
  321.     }
  322.    
  323.     # 将用户添加到wheel组(sudo权限)
  324.     usermod -aG wheel "$USER_NAME" || warn "Failed to add user $USER_NAME to wheel group"
  325.    
  326.     # 保存密码到文件(实际应用中可能需要更安全的方式)
  327.     echo "User: $USER_NAME, Password: $PASSWORD" > "/home/$USER_NAME/credentials.txt"
  328.     chmod 600 "/home/$USER_NAME/credentials.txt"
  329.     chown "$USER_NAME:$USER_NAME" "/home/$USER_NAME/credentials.txt"
  330.    
  331.     info "User $USER_NAME created successfully"
  332. }
  333. # 设置SSH(如果配置为true)
  334. setup_ssh() {
  335.     if [ "$ENABLE_SSH" != "true" ]; then
  336.         info "SSH setup skipped (disabled in configuration)"
  337.         return 0
  338.     fi
  339.    
  340.     info "Setting up SSH"
  341.    
  342.     # 安装OpenSSH服务器
  343.     dnf install -y openssh-server || {
  344.         error "Failed to install OpenSSH server"
  345.         return 1
  346.     }
  347.    
  348.     # 备份原始配置文件
  349.     cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
  350.    
  351.     # 配置SSH
  352.     cat > /etc/ssh/sshd_config << EOF
  353. Port $SSH_PORT
  354. PermitRootLogin no
  355. PasswordAuthentication yes
  356. PubkeyAuthentication yes
  357. AuthorizedKeysFile .ssh/authorized_keys
  358. ChallengeResponseAuthentication no
  359. UsePAM yes
  360. X11Forwarding yes
  361. PrintMotd no
  362. AcceptEnv LANG LC_*
  363. Subsystem sftp /usr/libexec/openssh/sftp-server
  364. EOF
  365.    
  366.     # 如果创建了用户,为用户设置SSH目录
  367.     if [ "$CREATE_USER" = "true" ] && [ -d "/home/$USER_NAME" ]; then
  368.         mkdir -p "/home/$USER_NAME/.ssh"
  369.         touch "/home/$USER_NAME/.ssh/authorized_keys"
  370.         chmod 700 "/home/$USER_NAME/.ssh"
  371.         chmod 600 "/home/$USER_NAME/.ssh/authorized_keys"
  372.         chown -R "$USER_NAME:$USER_NAME" "/home/$USER_NAME/.ssh"
  373.     fi
  374.    
  375.     # 启用并启动SSH服务
  376.     systemctl enable --now sshd || {
  377.         error "Failed to enable and start SSH service"
  378.         return 1
  379.     }
  380.    
  381.     info "SSH setup completed"
  382. }
  383. # 优化系统性能
  384. optimize_system() {
  385.     info "Optimizing system performance"
  386.    
  387.     # 创建sysctl配置文件
  388.     cat > /etc/sysctl.d/99-sysctl.conf << EOF
  389. # Increase file descriptor limit
  390. fs.file-max = 100000
  391. # Network optimization
  392. net.core.rmem_max = 16777216
  393. net.core.wmem_max = 16777216
  394. net.ipv4.tcp_rmem = 4096 87380 16777216
  395. net.ipv4.tcp_wmem = 4096 65536 16777216
  396. net.ipv4.tcp_fin_timeout = 30
  397. net.ipv4.tcp_keepalive_time = 120
  398. net.ipv4.tcp_max_syn_backlog = 65536
  399. net.core.netdev_max_backlog = 65536
  400. # Swap optimization
  401. vm.swappiness = 10
  402. vm.vfs_cache_pressure = 50
  403. EOF
  404.    
  405.     # 应用sysctl设置
  406.     sysctl -p /etc/sysctl.d/99-sysctl.conf || warn "Failed to apply sysctl settings"
  407.    
  408.     # 设置文件描述符限制
  409.     echo "* soft nofile 100000" >> /etc/security/limits.conf
  410.     echo "* hard nofile 100000" >> /etc/security/limits.conf
  411.    
  412.     info "System optimization completed"
  413. }
  414. # 显示部署摘要
  415. show_summary() {
  416.     info "Deployment Summary"
  417.     info "=================="
  418.     info "System: $(cat /etc/fedora-release)"
  419.     info "Kernel: $(uname -r)"
  420.     info "Architecture: $(uname -m)"
  421.     info ""
  422.    
  423.     if [ "$INSTALL_GUI" = "true" ]; then
  424.         info "GUI: Installed"
  425.     else
  426.         info "GUI: Not installed"
  427.     fi
  428.    
  429.     if [ "$INSTALL_DEV_TOOLS" = "true" ]; then
  430.         info "Development Tools: Installed"
  431.     else
  432.         info "Development Tools: Not installed"
  433.     fi
  434.    
  435.     if [ "$SETUP_FIREWALL" = "true" ]; then
  436.         info "Firewall: Enabled"
  437.     else
  438.         info "Firewall: Not enabled"
  439.     fi
  440.    
  441.     if [ "$CREATE_USER" = "true" ]; then
  442.         info "User: $USER_NAME created"
  443.         if [ -f "/home/$USER_NAME/credentials.txt" ]; then
  444.             info "Credentials saved to /home/$USER_NAME/credentials.txt"
  445.         fi
  446.     else
  447.         info "User: Not created"
  448.     fi
  449.    
  450.     if [ "$ENABLE_SSH" = "true" ]; then
  451.         info "SSH: Enabled on port $SSH_PORT"
  452.     else
  453.         info "SSH: Not enabled"
  454.     fi
  455.    
  456.     info ""
  457.     info "Deployment completed successfully!"
  458. }
  459. # =============================================================================
  460. # 主函数
  461. # =============================================================================
  462. main() {
  463.     # 初始化
  464.     rotate_log
  465.     info "Starting Fedora deployment script"
  466.    
  467.     # 检查运行环境
  468.     check_root
  469.    
  470.     # 加载并验证配置
  471.     load_config
  472.     validate_config
  473.    
  474.     # 创建临时目录
  475.     TEMP_DIR=$(mktemp -d)
  476.     debug "Created temporary directory: $TEMP_DIR"
  477.    
  478.     # 执行部署步骤
  479.     update_system
  480.     install_base_packages
  481.     install_gui
  482.     install_dev_tools
  483.     setup_firewall
  484.     create_user
  485.     setup_ssh
  486.     optimize_system
  487.    
  488.     # 显示部署摘要
  489.     show_summary
  490.    
  491.     # 清理
  492.     cleanup
  493.    
  494.     info "Fedora deployment script completed successfully"
  495.     exit 0
  496. }
  497. # 运行主函数
  498. main
复制代码

最佳实践和注意事项

在编写Fedora自动化部署脚本时,遵循一些最佳实践可以帮助你创建更可靠、更安全的脚本。

代码组织和可读性

1. 使用函数模块化代码:将相关功能组织到函数中,使代码更易于理解和维护。
  1. # 不好的做法
  2.    echo "Updating system..."
  3.    dnf update -y
  4.    echo "Installing packages..."
  5.    dnf install -y package1 package2
  6.    
  7.    # 好的做法
  8.    update_system() {
  9.        echo "Updating system..."
  10.        dnf update -y
  11.    }
  12.    
  13.    install_packages() {
  14.        echo "Installing packages..."
  15.        dnf install -y package1 package2
  16.    }
  17.    
  18.    update_system
  19.    install_packages
复制代码

1. 使用有意义的变量名:避免使用单字母变量名,使用描述性名称。
  1. # 不好的做法
  2.    d=$1
  3.    f=$2
  4.    
  5.    # 好的做法
  6.    DIRECTORY=$1
  7.    FILENAME=$2
复制代码

1. 添加注释:为复杂的代码段添加注释,解释其功能和目的。
  1. # 检查文件是否存在并可读
  2.    if [ -f "$CONFIG_FILE" ] && [ -r "$CONFIG_FILE" ]; then
  3.        source "$CONFIG_FILE"
  4.    fi
复制代码

错误处理和健壮性

1. 使用set命令:使用set -euo pipefail使脚本在遇到错误时立即退出。
  1. #!/bin/bash
  2.    set -euo pipefail
复制代码

1. 检查命令是否存在:在使用命令前检查它是否存在。
  1. if command -v dnf >/dev/null 2>&1; then
  2.        dnf update -y
  3.    else
  4.        echo "DNF package manager not found"
  5.        exit 1
  6.    fi
复制代码

1. 验证输入:验证用户输入和配置参数。
  1. # 验证端口号
  2.    if ! [[ "$PORT" =~ ^[0-9]+$ ]] || [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ]; then
  3.        echo "Invalid port number: $PORT"
  4.        exit 1
  5.    fi
复制代码

安全性考虑

1. 避免硬编码敏感信息:不要在脚本中硬编码密码、API密钥等敏感信息。
  1. # 不好的做法
  2.    PASSWORD="secret123"
  3.    
  4.    # 好的做法
  5.    read -s -p "Enter password: " PASSWORD
复制代码

1. 安全地处理临时文件:使用mktemp创建临时文件,并在使用后删除。
  1. TEMP_FILE=$(mktemp)
  2.    # 使用临时文件...
  3.    rm -f "$TEMP_FILE"
复制代码

1. 限制文件权限:确保敏感文件具有适当的权限。
  1. touch "$CONFIG_FILE"
  2.    chmod 600 "$CONFIG_FILE"  # 仅所有者可读写
复制代码

性能优化

1. 避免不必要的命令:减少不必要的命令调用,特别是在循环中。
  1. # 不好的做法
  2.    for i in {1..100}; do
  3.        CURRENT_DIR=$(pwd)
  4.        echo "Processing in $CURRENT_DIR"
  5.    done
  6.    
  7.    # 好的做法
  8.    CURRENT_DIR=$(pwd)
  9.    for i in {1..100}; do
  10.        echo "Processing in $CURRENT_DIR"
  11.    done
复制代码

1. 使用内置Shell功能:优先使用Shell内置功能,而不是外部命令。
  1. # 不好的做法
  2.    LENGTH=$(echo "$STRING" | wc -c)
  3.    
  4.    # 好的做法
  5.    LENGTH=${#STRING}
复制代码

1. 并行处理:对于可以并行执行的任务,使用后台处理。
  1. # 顺序处理
  2.    process_file file1
  3.    process_file file2
  4.    process_file file3
  5.    
  6.    # 并行处理
  7.    process_file file1 &
  8.    process_file file2 &
  9.    process_file file3 &
  10.    wait  # 等待所有后台任务完成
复制代码

可维护性和可扩展性

1. 使用配置文件:将配置参数与脚本逻辑分离,使用外部配置文件。
  1. # 加载配置文件
  2.    if [ -f "$CONFIG_FILE" ]; then
  3.        source "$CONFIG_FILE"
  4.    else
  5.        echo "Configuration file not found: $CONFIG_FILE"
  6.        exit 1
  7.    fi
复制代码

1. 提供帮助信息:为脚本添加帮助信息,说明如何使用。
  1. show_help() {
  2.        echo "Usage: $0 [OPTIONS]"
  3.        echo "Options:"
  4.        echo "  -c, --config FILE    Use specified configuration file"
  5.        echo "  -h, --help           Show this help message"
  6.        echo "  -v, --verbose        Enable verbose output"
  7.    }
  8.    
  9.    # 解析命令行参数
  10.    while [[ $# -gt 0 ]]; do
  11.        case "$1" in
  12.            -c|--config)
  13.                CONFIG_FILE="$2"
  14.                shift 2
  15.                ;;
  16.            -h|--help)
  17.                show_help
  18.                exit 0
  19.                ;;
  20.            -v|--verbose)
  21.                VERBOSE=true
  22.                shift
  23.                ;;
  24.            *)
  25.                echo "Unknown option: $1"
  26.                show_help
  27.                exit 1
  28.                ;;
  29.        esac
  30.    done
复制代码

1. 版本控制:使用版本控制系统(如Git)管理脚本,记录变更历史。

测试和调试

1. 添加调试模式:为脚本添加调试模式,输出详细的执行信息。
  1. DEBUG=false
  2.    
  3.    debug() {
  4.        if [ "$DEBUG" = "true" ]; then
  5.            echo "DEBUG: $*" >&2
  6.        fi
  7.    }
  8.    
  9.    # 使用调试
  10.    debug "Processing file: $FILENAME"
复制代码

1. 使用shellcheck:使用shellcheck工具检查脚本中的常见问题。
  1. # 安装shellcheck
  2.    dnf install -y shellcheck
  3.    
  4.    # 检查脚本
  5.    shellcheck your_script.sh
复制代码

1. 分步测试:在部署到生产环境之前,先在测试环境中分步测试脚本。
  1. # 在脚本中添加测试模式
  2.    if [ "$TEST_MODE" = "true" ]; then
  3.        echo "Running in test mode"
  4.        # 添加测试逻辑...
  5.    fi
复制代码

结论

Fedora自动化部署脚本是系统管理员和开发人员的强大工具,能够显著提高部署效率,减少人为错误,确保系统配置的一致性。本文从基础命令开始,逐步深入到高级应用,全面解析了Fedora自动化部署脚本的编写技巧。

通过掌握这些技术,你可以创建功能强大、健壮可靠的自动化部署脚本,满足各种复杂的部署需求。记住,好的脚本不仅要能够完成任务,还要易于维护、扩展和理解。遵循最佳实践,持续改进你的脚本,它们将成为你管理Fedora系统的得力助手。

随着技术的不断发展,自动化部署领域也在不断演进。保持学习,探索新的工具和技术,如Ansible、Docker和Kubernetes,将帮助你构建更加现代化、高效的部署解决方案。无论技术如何变化,本文介绍的基本原则和技巧都将是你宝贵的财富。
「七転び八起き(ななころびやおき)」
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则