05-流程控制之case

为什么学case语句

在Bash编程中,学习case语句是很有必要的,主要有以下几个原因:

实现多分支逻辑

  • 在Bash脚本中,经常需要根据不同的条件执行不同的操作。case语句提供了一种清晰、简洁的方式来处理多分支逻辑,比使用多个if-else语句更加直观和易读。例如,根据用户输入的不同选项来执行不同的命令或函数:
    read -p "请输入选项(1、2或3):" choice
    case $choice in
      1)
          echo "你选择了选项1"
          # 在这里添加选项1要执行的命令或函数
          ;;
      2)
          echo "你选择了选项2"
          # 在这里添加选项2要执行的命令或函数
          ;;
      3)
          echo "你选择了选项3"
          # 在这里添加选项3要执行的命令或函数
          ;;
      *)
          echo "无效的选项"
          ;;
    esac
    

getopts命令

getopts是Unix和类Unix系统中用于处理命令行参数的命令,通常在Shell脚本中使用,以下是关于它的详细介绍:

基本语法

getopts optstring name [arg]

  • optstring:定义了合法的选项字符。如果选项字符后面跟有冒号:,表示该选项需要有一个参数。例如,ab:c表示合法的选项有abc,其中b选项需要有一个参数。
  • name:用于在脚本中存储当前选项的变量名。在脚本中,可以通过这个变量来获取当前处理的选项字符。
  • arg:通常是$@,表示传递给脚本的所有参数。如果省略arg,则getopts会默认使用$*

工作原理

getopts命令会逐个处理命令行参数中的选项。它会在optstring中查找与命令行选项匹配的字符,并根据其规则进行处理。对于每个选项,getopts会将选项字符存储到指定的变量name中,并根据选项是否需要参数进行相应的操作。如果选项需要参数,getopts会将参数存储到特殊变量OPTARG中。当所有选项都处理完毕后,getopts会返回一个非零值,表示处理结束。

示例

以下是一个简单的Shell脚本示例,演示了如何使用getopts处理命令行参数:

#!/bin/bash

while getopts "ab:c" opt; do
    case $opt in
        a)
            echo "选项a被设置"
            ;;
        b)
            echo "选项b被设置,参数为:$OPTARG"
            ;;
        c)
            echo "选项c被设置"
            ;;
        \?)
            echo "无效的选项:-$OPTARG" >&2
            exit 1
            ;;
    esac
done

在上述脚本中,while循环使用getopts处理命令行参数。getoptsoptstring"ab:c",表示支持abc三个选项,其中b选项需要一个参数。在case语句中,根据不同的选项进行相应的处理。如果遇到无效的选项,会输出错误信息并退出脚本。

你可以使用以下方式调用这个脚本:

./test.sh -a -b hello -c

输出结果为:

选项a被设置
选项b被设置,参数为:hello
选项c被设置

常见用途

  • 实现命令行工具的选项解析:在编写命令行工具时,getopts可以用于解析用户输入的各种选项,从而实现不同的功能。例如,一个文件处理工具可能支持-f选项用于指定输入文件,-o选项用于指定输出文件等。
  • 配置脚本的参数处理:在一些需要配置参数的脚本中,getopts可以用于处理用户通过命令行传递的配置参数。比如,一个服务器启动脚本可能支持-p选项用于指定端口号,-c选项用于指定配置文件路径等。

处理命令行参数

当编写需要接受命令行参数的脚本时,case语句可以方便地对不同的参数进行处理。例如,一个脚本可能有不同的操作模式,通过命令行参数来指定,使用case语句可以很容易地实现根据不同参数执行相应的操作。

while getopts:

  • getopts 是用来解析命令行选项的 Bash 内置命令。
  • "a:b:c:" 表示脚本支持 -a-b-c 选项,每个选项后面都需要一个参数(因为冒号 : 表示需要参数)。

循环解析选项:

  • while getopts 会按顺序解析命令行输入的选项和参数。
  • 变量 $opt 会存储当前选项的名称。
  • $OPTARG 用于存储当前选项的参数值。

case 分支:

  • 针对不同的选项 (a, b, c) 执行对应的处理逻辑,输出其参数值。
  • \? 匹配无效选项,输出错误信息并以状态码 1 退出脚本。
#!/bin/bash

show_help() {
    echo "用法: $0 [-a 参数] [-b 参数] [-c 参数]"
    echo "选项:"
    echo "  -a    设置参数 a"
    echo "  -b    设置参数 b"
    echo "  -c    设置参数 c"
    echo "  -h    显示帮助信息"
}

while getopts "a:b:c:h" opt; do
        echo "程序开始.."
    case $opt in
        a)
            if [ -z "$OPTARG" ]; then
                echo "错误:选项 -a 需要一个参数" >&2
                exit 1
            fi
            echo "选项a的值为:$OPTARG"
            ;;
        b)
            if [ -z "$OPTARG" ]; then
                echo "错误:选项 -b 需要一个参数" >&2
                exit 1
            fi
            echo "选项b的值为:$OPTARG"
            ;;
        c)
            if [ -z "$OPTARG" ]; then
                echo "错误:选项 -c 需要一个参数" >&2
                exit 1
            fi
            echo "选项c的值为:$OPTARG"
            ;;
        h)
            show_help
            exit 0
            ;;
        \?)
            echo "错误:无效的选项 -$OPTARG" >&2
            show_help
            exit 1
            ;;
    esac
done

提高脚本的可维护性和可读性

  • 对于复杂的逻辑流程,使用case语句可以将不同的情况分组并清晰地展示出来。这样,当其他人阅读或维护脚本时,能够快速理解脚本的功能和逻辑结构。比如,在一个系统管理脚本中,根据不同的操作类型来执行相应的系统命令,使用case语句可以使代码结构更加清晰,易于维护。
    operation=$1
    case $operation in
      "start")
          systemctl start httpd
          ;;
      "stop")
          systemctl stop httpd
          ;;
      "restart")
          systemctl restart httpd
          ;;
      *)
          echo "无效的操作。请使用start、stop或restart。"
          ;;
    esac
    

与其他语言特性配合

  • Bash中的case语句可以与其他语言特性,如函数、变量、循环等结合使用,形成更强大、更灵活的脚本。例如,可以在函数中使用case语句来根据不同的条件返回不同的值,或者在循环中使用case语句来处理不同类型的元素。

总之,case语句是Bash编程中的一个重要工具,它能够帮助开发者更高效地处理多分支逻辑,提高脚本的质量和可维护性,是编写复杂Bash脚本不可或缺的一部分。

1.case语句作用

case和if一样,都是用于处理多分支的条件判断

但是在条件较多的情况,if嵌套太多就不够简洁了

case语句就更简洁和规范了

2.case用法参考

image-20220619134239037

常见用法就是如根据用户输入的参数来匹配,执行不同的操作。

最常见的就是如服务脚本的 {start|restart|stop|reload} 这样的操作判断

3.case基本语法

在Bash中,case语句的基本语法如下:

case 变量 in
    模式1)
        # 当变量匹配模式1时执行的命令
        ;;
    模式2)
        # 当变量匹配模式2时执行的命令
        ;;
   ...
    模式n)
        # 当变量匹配模式n时执行的命令
        ;;
    *)
        # 当变量不匹配任何上述模式时执行的命令
        ;;
esac

解释说明

  • case关键字:表示开始一个case语句。
  • 变量:通常是一个变量或表达式,其值将与下面列出的各个模式进行比较。
  • in关键字:用于分隔要比较的变量和模式列表。
  • 模式1)... ;;:这是一个模式匹配的单元。
    • 模式1:可以是一个具体的值、一个通配符表达式或者是一个正则表达式。
    • ):表示模式的结束。
    • # 当变量匹配模式1时执行的命令:这里是当变量匹配该模式时要执行的一系列Bash命令。
    • ;;:表示该模式匹配的命令执行结束,类似于其他语言中的break,表示终止当前分支的执行,防止继续匹配后续模式。
  • *)... ;;:这是一个特殊的模式,它匹配任何不匹配上述模式的情况,类似于default语句在其他语言中的作用。
  • esac:表示case语句的结束,是case的反写。

模式匹配规则

  • 具体值:如果模式是一个具体的值,变量必须完全等于该值才能匹配,例如 case $var in "value")...
  • 通配符:
    • *:匹配任意字符序列,可用于匹配多个字符。例如 case $var in "prefix*")... 可以匹配以 prefix 开头的任何字符串。
    • ?:匹配单个任意字符。例如 case $var in "prefix?")... 可以匹配 prefixaprefixb 等。
    • [abc]:匹配 abc 中的任意一个字符。例如 case $var in "[abc]")... 可以匹配 abc
    • [a-z]:匹配 az 之间的任意一个字符。例如 case $var in "[a-z]")... 可以匹配 az 之间的任意一个小写字母。

示例代码

fruit="apple"
case $fruit in
    "apple")
        echo "This is an apple."
        ;;
    "banana")
        echo "This is a banana."
        ;;
    "cherry")
        echo "This is a cherry."
        ;;
    *)
        echo "This is some other fruit."
        ;;
esac

代码解释

  • 上述代码中,变量 fruit 的值为 "apple"
  • 程序将 fruit 的值依次与 "apple""banana""cherry" 进行比较。
  • fruit 的值为 "apple" 时,匹配第一个模式,执行 echo "This is an apple." 并使用 ;; 终止该分支。
  • 如果 fruit 的值为 "banana""cherry",会分别执行相应分支的 echo 语句。
  • 如果 fruit 的值不是上述三种水果,将匹配 *) 模式,执行 echo "This is some other fruit."

注意事项

  • 确保每个模式分支都以 ;; 结尾,以避免执行后续的模式分支。
  • 模式可以使用简单的字符串匹配或更复杂的通配符,但要注意通配符的使用范围,避免匹配到意外的结果。
  • 在复杂脚本中,合理使用 case 语句可以提高代码的可读性和可维护性,避免过多的 if-else 语句造成的混乱。

4.if和case的区别实践

以下是 ifcase 在 Bash 中的区别实践:

1. 语法结构

  • if 语句
    if [ 条件表达式1 ]; then
      # 条件表达式1 为真时执行的命令
    elif [ 条件表达式2 ]; then
      # 条件表达式2 为真时执行的命令
    elif [ 条件表达式3 ]; then
      # 条件表达式3  为真时执行的命令    
    else
      # 以上条件都不满足时执行的命令
    fi
    
  • case 语句
    case 变量 in
      模式1)
          # 变量匹配模式1时执行的命令
          ;;
      模式2)
          # 变量匹配模式2时执行的命令
          ;;
      *)
          # 变量不匹配任何模式时执行的命令
          ;;
    esac
    

2. 适用场景

  • if 语句
    • 适用于检查简单或复杂的条件,例如文件是否存在、变量是否为空、数字大小比较等。
    • 可以使用逻辑运算符(如 &&||!)来组合多个条件。
    • 适合对条件进行范围检查或进行逻辑计算后的条件判断。
      if [ -f "file.txt" ] && [ -r "file.txt" ]; then
        echo "文件存在且可读"
      elif [ -f "file.txt" ] && [! -r "file.txt" ]; then
        echo "文件存在但不可读"
      else
        echo "文件不存在"
      fi
      
      上述代码中,首先检查文件 file.txt 是否存在且可读,然后检查文件是否存在但不可读,最后处理文件不存在的情况。
  • case 语句
    • 适用于对变量的不同取值进行判断,特别是当变量可能有多个离散的取值时。
    • 对于处理命令行参数或用户输入的选项非常方便。
    • 模式匹配更直观,适用于检查变量是否等于某个特定值或匹配某种模式。
      read -p "请输入选项 (start|stop|restart): " option
      case $option in
        start)
            echo "启动服务"
            ;;
        stop)
            echo "停止服务"
            ;;
        restart)
            echo "重启服务"
            ;;
        *)
            echo "无效的选项"
            ;;
      esac
      
      上述代码中,根据用户输入的不同选项(start、stop、restart)执行不同的操作,使用 case 可以清晰地列出不同的选项及其对应的操作。

3. 性能考虑

  • if 语句
    • 对于多个 if-elif-else 语句,可能会对同一个条件进行多次检查,尤其是在复杂的逻辑组合中。
    • 性能可能会受到复杂条件表达式和逻辑运算符的影响。
    • 适用于少量的、复杂的条件判断,例如需要同时考虑文件权限、大小、修改时间等多个维度的情况。
  • case 语句
    • 对于多个离散的选项,性能通常较好,因为只需要一次匹配操作。
    • 更简洁和易读,尤其在处理用户输入或命令行参数时,只需要将输入与不同的模式匹配一次。

4. 可维护性

  • if 语句
    • 对于多个 if-elif-else 语句,代码可能会变得冗长和难以阅读,特别是当条件复杂时。
    • 维护时需要仔细分析逻辑表达式和运算符,以确保逻辑正确。
    • 适合需要复杂逻辑判断和条件组合的情况,但可能会使代码结构较复杂。
  • case 语句
    • 对于处理多个离散选项,代码结构更清晰,易于阅读和维护。
    • 可以方便地添加、删除或修改模式及其对应的操作,而不影响其他部分。

5. 灵活性

  • if 语句
    • 可以处理更广泛的条件,包括文件测试、变量测试、算术比较等。
    • 可根据不同的环境变量或文件属性做出不同的决策。
      if (( $a > $b )); then
        echo "$a 大于 $b"
      elif (( $a < $b )); then
        echo "$a 小于 $b"
      else
        echo "$a 等于 $b"
      fi
      
      上述代码使用算术比较来判断两个变量的大小关系。
  • case 语句
    • 更侧重于字符串匹配和模式匹配。
    • 对于处理字符串类型的变量或命令行参数,提供更简洁的模式匹配功能。
      str="abc"
      case $str in
        a*)
            echo "以 a 开头"
            ;;
        *c)
            echo "以 c 结尾"
            ;;
        *)
            echo "其他情况"
            ;;
      esac
      
      上述代码使用模式匹配来判断字符串的特征。

在实际使用中,根据具体的需求和场景选择 ifcase 语句:

  • 当需要处理多个范围条件、文件属性检查或复杂逻辑时,使用 if 语句。
  • 当需要处理离散的选项、字符串匹配或用户输入时,使用 case 语句。

总之,合理使用 ifcase 语句可以使你的 Bash 脚本更加清晰、高效和易于维护。

4.1 脚本需求

根据用户选择执行不同的操作

4.2 if写法


#!/bin/bash
echo -e "-------
1. 取钱
2. 存钱
3. 查余额
-----"

read -p "请输入你的操作:"  num

if [ $num -eq 1 ];then
        echo "取了5万"
elif [ $num -eq 2 ];then
    echo "存了10万"
elif [ $num -eq 3 ];then
    echo "余额还剩下60万"
else
    echo "能不能长点心,按照要求输入?"
fi

4.3 case写法

  • 明显的,if多条件判断,会存在重复性的代码,case简化了操作
  • 而且还省去了出现if条件判断语法,导致的各种异常
#!/bin/bash
echo -e "-------
1. 取钱
2. 存钱
3. 查余额
-----"

read -p "请输入你的操作:"  num

case $num in
1)
    echo "取了5万"
    ;;
2)
    echo "存了10万"
    ;;
3)
    echo "余额还剩下60万"
    ;;

*)
    echo "能不能长点心,按照要求输入?"
esac

5.使用case写出更健壮的计算器

需求(也是开发脚本的功能思路,在你很熟练语法之后,你看到这样的需求,心中应该理解想到,用哪些shell脚本的语法即可完成。)

1. 交互式接收用户输入的数字,计算符号
2. 判断用户输入的参数是否是3个
3. 判断用户输入的是否是纯数字整数
4. 判断用户输入的计算符号是否是 加减乘除
5. 如果用户输入错误,友好提示用户正确输入的语法
6. 如果输入错误,程序无须结束,循环重来让用户输入(循环知识点,以后做)
7. 重复性的代码,封装为函数(以后做)

代码

#!/bin/bash
# 数字1
read -p "请输入要计算的数字1:" num1
is_num=$(echo $num1 | sed -r "s#[0-9]+##g")

# 如果字符串非空,也就是并非纯数字
if [ ! -z $is_num ];then
    echo "请输入纯数字整数!!"
    exit 1
fi
# 66



# 数字2
read -p "请输入要计算的数字2:" num2
is_num=$(echo $num2 | sed -r "s#[0-9]+##g")

# 如果字符串非空,也就是并非纯数字
if [ ! -z $is_num ];then
    echo "请输入纯数字整数!!"
    exit
fi


echo -e "请选择运算符号:
1. +
2. -
3. *
4. /"


read -p "请输入您的符号选择:" sign

case $sign in 
1)
    echo "$num1 + $num2 = $(( $num1 + $num2 ))"
    ;;
2)
    echo "$num1 - $num2 = $(( $num1 - $num2 ))"
    ;;
3)
    echo "$num1 * $num2 = $(( $num1 * $num2 ))"
    ;;
4)
    echo "$num1 / $num2 = $(( $num1 / $num2 ))"
    ;;
*)
    echo "请您输入 1~4的选项。"

esac

参考答案-1

read命令解释

  • read -p "请输入表达式(格式:数字 运算符 数字): " input
    • read 是一个命令,用于从标准输入读取用户输入的数据。
    • -p "请输入表达式(格式:数字 运算符 数字): "read 命令的一个选项,它会在读取输入之前显示指定的提示信息,即 "请输入表达式(格式:数字 运算符 数字): "
    • input 是用户输入的数据将被存储的变量名。用户输入的内容会被存储在 input 变量中。
  • IFS=' ' read -ra parts <<< "$input"
    • IFS=' 'IFS 代表内部字段分隔符(Internal Field Separator),这里将其设置为一个空格。IFS 用于确定 read 命令如何将输入的字符串拆分成多个部分。
    • read -ra parts
      • -r 选项告诉 read 命令以原始模式读取输入,即不允许反斜杠转义。
      • -a parts 选项告诉 read 命令将输入的数据存储到一个数组中,数组的名称是 parts
    • <<< "$input":这是一个“Here String”,它将 input 变量的值作为 read 命令的输入源。

这段代码的整体功能是提示用户输入一个表达式,表达式的格式为“数字 运算符 数字”,用户输入后,代码会将用户输入的表达式以空格为分隔符拆分成一个数组,并将数组存储在 parts 变量中。例如,如果用户输入 5 + 3,那么 parts 数组将包含三个元素,分别是 ["5", "+", "3"]。这样做方便后续对表达式中的数字和运算符进行单独处理,例如可以根据运算符进行相应的计算操作,通过访问 parts[0] 可以获取第一个数字,parts[1] 可以获取运算符,parts[2] 可以获取第二个数字。

假设以下是一个使用该代码的示例:

read -p "请输入表达式(格式:数字 运算符 数字): " input
IFS=' ' read -ra parts <<< "$input"
echo "第一个数字是: ${parts[0]}"
echo "运算符是: ${parts[1]}"
echo "第二个数字是: ${parts[2]}"

在这个示例中,用户输入表达式后,程序会将输入拆分成数组并存储在 parts 变量中,然后分别输出表达式中的第一个数字、运算符和第二个数字。这为后续进行更复杂的操作,如根据运算符进行相应的计算等提供了便利。

#!/bin/bash

# 检查输入是否为纯数字整数
is_integer() {
        # -?
        # -69
        #  69
    if [[ $1 =~ ^-?[0-9]+$ ]]; then
        return 0
    else
        return 1
    fi
}

# 主程序
while true; do
    read -p "请输入表达式(格式:数字 运算符 数字): " input
    # 将输入拆分成数组
    IFS=' ' read -ra parts <<< "$input"

    # 检查输入参数是否为 3 个
    if [ ${#parts[@]} -ne 3 ]; then
        echo "输入错误,请使用正确的格式:数字 运算符 数字"
        continue
    fi
    # 3 + 2 
    num1=${parts[0]}
    operator=${parts[1]}
    num2=${parts[2]}

    # 检查输入是否为纯数字整数
    if ! is_integer $num1 ||! is_integer $num2; then
        echo "输入错误,数字必须是整数。"
        continue
    fi

    # 检查运算符是否合法
    case $operator in
        +)
            result=$((num1 + num2))
            ;;
        -)
            result=$((num1 - num2))
            ;;
           \*)
            result=$((num1 * num2))
            ;;

        /)
            if [ $num2 -eq 0 ]; then
                echo "除数不能为 0。"
                continue
            else
                result=$((num1 / num2))
            fi
            ;;
         *)
            echo "输入错误,运算符必须是 +、-、* 或 /。"
            continue
            ;;
    esac

    echo "结果: $result"
done

代码解释

  1. 函数 is_integer
    • 该函数使用 =~ 运算符和正则表达式 ^-?[0-9]+$ 来检查输入的字符串是否为纯数字整数。
    • ^ 表示字符串的开始,-? 表示可选的负号,[0-9]+ 表示一个或多个数字,$ 表示字符串的结束。
    • 如果匹配成功,函数返回 0(true),否则返回 1(false)。
  2. 主程序部分
    • 使用 while true 实现一个无限循环,直到用户输入正确的表达式并计算出结果。
    • read -p "请输入表达式(格式:数字 运算符 数字): " input:提示用户输入表达式并读取输入。
    • IFS=' ' read -ra parts <<< "$input":使用 IFS(内部字段分隔符)将输入按空格拆分成数组 parts
    • if [ ${#parts[@]} -ne 3 ]; then...:检查输入的元素数量是否为 3,如果不是,提示用户输入错误并使用 continue 重新开始循环。
    • if! is_integer $num1 ||! is_integer $num2; then...:调用 is_integer 函数检查输入的数字是否为整数,如果不是,提示用户并重新开始循环。
    • case $operator in...:使用 case 语句检查运算符是否合法。
      • +- 情况:使用 $((num1 + num2))$((num1 - num2)) 进行简单的加、减运算。
      • *) 情况:对于不合法的运算符,提示用户并重新开始循环。
      • / 情况:进行除法运算,但先检查除数是否为 0,如果为 0,提示用户除数不能为 0 并重新开始循环。
    • echo "结果: $result":输出计算结果。

使用方法

  1. 将上述代码保存为一个文件,例如 calculator.sh
  2. 给文件添加可执行权限:chmod +x calculator.sh
  3. 运行脚本:./calculator.sh
  4. 按照提示输入表达式,例如 5 + 3,脚本将计算结果并输出。

这个脚本通过函数和 case 语句,结合循环,确保了输入的合法性和程序的健壮性。它可以不断提示用户输入正确的表达式,直到计算出结果,同时对各种可能的错误进行了检查和处理。

请注意,这个脚本还可以进一步优化,例如支持浮点数运算,处理更多的错误情况,以及添加更多的运算符等。

参考答案-2

以下是一个更健壮的交互式 Bash 计算器脚本,采用 case 和函数来提高可读性和可维护性:

#!/bin/bash

# 检查输入是否为纯整数
is_integer() {
    [[ $1 =~ ^-?[0-9]+$ ]]
}

# 检查运算符是否有效
is_operator() {
    case $1 in
        +|-|*|/) return 0 ;;
        *) return 1 ;;
    esac
}

# 执行计算
calculate() {
    local num1=$1
    local operator=$2
    local num2=$3

    case $operator in
        +) echo $((num1 + num2)) ;;
        -) echo $((num1 - num2)) ;;
        *) echo $((num1 * num2)) ;;
        /)
            if [[ $num2 -eq 0 ]]; then
                echo "错误: 除数不能为0"
            else
                echo $((num1 / num2))
            fi
            ;;
    esac
}

# 主逻辑循环
while true; do
    echo "请输入计算公式(格式: 数字1 运算符 数字2,例如:3 + 5):"
    read -r num1 operator num2

    # 检查输入参数数量
    if [[ -z $num1 || -z $operator || -z $num2 ]]; then
        echo "错误: 请输入完整的三个参数(数字 运算符 数字)。"
        continue
    fi

    # 检查是否为整数
    if ! is_integer "$num1" || ! is_integer "$num2"; then
        echo "错误: 请输入有效的整数。"
        continue
    fi

    # 检查运算符
    if ! is_operator "$operator"; then
        echo "错误: 请输入有效的运算符(+ - * /)。"
        continue
    fi

    # 计算并输出结果
    result=$(calculate "$num1" "$operator" "$num2")
    echo "结果: $num1 $operator $num2 = $result"
done

脚本说明:

  1. 函数封装

    • is_integer: 检查是否为纯整数。
    • is_operator: 检查运算符是否为 + - * /
    • calculate: 根据输入执行对应运算。
  2. 交互式输入

    • 使用 read 接收用户输入,并分离为三个变量。
  3. 输入验证

    • 确保输入有三个参数。
    • 验证数字是否合法。
    • 验证运算符是否合法。
  4. 循环重试

    • 使用 while true 实现循环,当输入错误时不会退出,提示用户重新输入。
  5. 友好提示

    • 针对不同错误场景提供详细提示。

运行此脚本时,它会持续运行,直到用户手动退出(如 Ctrl+C)。

6.开发非交互的服务启动脚本

# 需求,使用case开发非交互的服务管理脚本,添加颜色状态功能
# 具体开发思路,可以参考systemctl是如何帮你管理程序的即可。
# 这里的脚本,等于是管理nginx的脚本,一个服务单独一个脚本即可。

代码

#!/bin/bash
source /etc/init.d/functions

your_service=$1
case $your_service in
start)
        echo "${your_service} 启动中"
        sleep 1
        nginx
        if [ $? -eq 0 ];then
            action "${your_service} 启动成功" /bin/true
        else
            action "${your_service} 启动失败" /bin/false
        fi
        ;;
stop)
    echo "${your_service} 停止中"
    sleep 1
    nginx -s stop
    if [ $? -eq 0 ];then
        action "${your_service} 以停止" /bin/true
    else
        action "${your_service} 停止报错!!" /bin/false
    fi
    ;;

restart)
    echo "${your_service} 重启中"
    nginx -s stop
    sleep 1
    nginx
    if [ $? -eq 0 ];then
        action "nginx 重启成功" /bin/true
    else
        action "nginx 重启失败" /bin/false
    fi
    ;;
reload)
    nginx -s reload
    if [ $? -eq 0 ];then
        action "nginx正在重新加载" /bin/true
    else
        action "nginx 重新载入失败" /bin/false
    fi
    ;;
check)
    echo "检测 ${your_service}  语法中"
    nginx -t
    ;;
status)
    echo "检查 ${your_service} 运行状态中"
    if [ ! -f "/run/nginx.pid" ];then
        echo "nginx未运行!!"
    else 
        echo "nginx运行中!进程id是$(cat /run/nginx.pid)"
    fi
;;
*)
    echo "用法错误,正确用法是:{start|stop|restart|reload|check}"
esac

参考答案1

以下是一个使用 case 开发的非交互式的 Nginx 服务管理脚本,并添加了颜色状态功能:

开发思路

  1. 使用 case 语句处理不同的操作,如 startstoprestartstatus 等。
  2. 对于 startstoprestart 操作,使用 systemctl 命令执行相应操作,并检查命令的执行结果,使用 $? 获取退出状态码,判断操作是否成功。
  3. 对于 status 操作,使用 systemctl status nginx 获取服务状态,并根据状态输出不同颜色的信息,使用 echo -e 命令输出彩色文本。
  4. 对于不支持的操作,给出相应的错误提示。
#!/bin/bash

# 定义颜色变量
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

case $1 in
    start)
        echo "正在启动 Nginx 服务..."
        systemctl start nginx
        if [ $? -eq 0 ]; then
            echo -e "${GREEN}Nginx 服务已成功启动。${NC}"
        else
            echo -e "${RED}启动 Nginx 服务失败。${NC}"
        fi
        ;;
    stop)
        echo "正在停止 Nginx 服务..."
        systemctl stop nginx
        if [ $? -eq 0 ]; then
            echo -e "${GREEN}Nginx 服务已成功停止。${NC}"
        else
            echo -e "${RED}停止 Nginx 服务失败。${NC}"
        fi
        ;;
    restart)
        echo "正在重启 Nginx 服务..."
        systemctl restart nginx
        if [ $? -eq 0 ]; then
            echo -e "${GREEN}Nginx 服务已成功重启。${NC}"
        else
            echo -e "${RED}重启 Nginx 服务失败。${NC}"
        fi
        ;;
    status)
        echo "正在检查 Nginx 服务状态..."
        systemctl status nginx > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo -e "${GREEN}Nginx 服务正在运行。${NC}"
        else
            echo -e "${YELLOW}Nginx 服务未运行。${NC}"
        fi
        ;;
    *)
        echo "不支持的操作。请使用 start、stop、restart 或 status。"
        ;;
esac

代码解释

  • 颜色变量
    • RED='\033[0;31m':定义红色的 ANSI 转义码。
    • GREEN='\033[0;32m':定义绿色的 ANSI 转义码。
    • YELLOW='\033[0;33m':定义黄色的 ANSI 转义码。
    • NC='\033[0m':定义无颜色的 ANSI 转义码,用于恢复正常颜色。
  • case 语句部分
    • start:使用 systemctl start nginx 启动 Nginx 服务,并通过 if [ $? -eq 0 ]; 检查 systemctl 命令的退出状态码。如果为 0,表示成功,输出绿色的成功消息,否则输出红色的失败消息。
    • stop:与 start 类似,但执行 systemctl stop nginx 来停止服务。
    • restart:使用 systemctl restart nginx 重启服务,并检查退出状态码。
    • status:使用 systemctl status nginx 检查服务状态,将输出重定向到 /dev/null 2>&1 以避免输出多余信息,根据状态码输出不同颜色的状态信息。
    • *):对于不支持的操作,输出错误提示。

使用方法

  1. 将上述脚本保存为一个文件,例如 nginx_service.sh
  2. 给文件添加可执行权限:chmod +x nginx_service.sh
  3. 执行不同的操作:
    • 启动服务:./nginx_service.sh start
    • 停止服务:./nginx_service.sh stop
    • 重启服务:./nginx_service.sh restart
    • 查看服务状态:./nginx_service.sh status

这个脚本使用 case 语句清晰地处理了不同的服务管理操作,同时使用颜色状态信息方便用户查看服务的状态和操作结果。根据实际需求,你可以扩展该脚本以支持更多的服务管理功能,如查看日志、修改配置等。

此外,需要注意的是,脚本中使用了 systemctl 命令,因此需要在使用 systemd 的系统上运行,并且需要有足够的权限执行相应的服务管理操作。

参考答案-2

以下是一个基于 case 的非交互式 Nginx 服务管理脚本,包含颜色状态功能,用于模仿 systemctl 的基本功能:

脚本代码

#!/bin/bash

# 颜色定义
GREEN="\033[32m"
RED="\033[31m"
YELLOW="\033[33m"
BLUE="\033[34m"
RESET="\033[0m"

# Nginx 服务控制命令(根据实际安装路径调整)
NGINX_CMD="/usr/sbin/nginx"
NGINX_PID_FILE="/var/run/nginx.pid"

# 打印带颜色的状态信息
print_status() {
    local status=$1
    local message=$2

    case $status in
        success) echo -e "${GREEN}[成功]${RESET} $message" ;;
        error) echo -e "${RED}[错误]${RESET} $message" ;;
        warning) echo -e "${YELLOW}[警告]${RESET} $message" ;;
        info) echo -e "${BLUE}[信息]${RESET} $message" ;;
    esac
}

# 检查 Nginx 是否运行
is_running() {
    if [[ -f $NGINX_PID_FILE && -s $NGINX_PID_FILE ]]; then
        if ps -p "$(cat "$NGINX_PID_FILE")" &>/dev/null; then
            return 0
        fi
    fi
    return 1
}

# 启动 Nginx
start_nginx() {
    if is_running; then
        print_status warning "Nginx 已经在运行 (PID: $(cat "$NGINX_PID_FILE"))。"
    else
        $NGINX_CMD &>/dev/null
        if is_running; then
            print_status success "Nginx 启动成功 (PID: $(cat "$NGINX_PID_FILE"))。"
        else
            print_status error "Nginx 启动失败,请检查配置或日志。"
        fi
    fi
}

# 停止 Nginx
stop_nginx() {
    if is_running; then
        kill "$(cat "$NGINX_PID_FILE")" &>/dev/null
        sleep 1
        if is_running; then
            print_status error "Nginx 停止失败,请手动检查进程。"
        else
            print_status success "Nginx 已停止。"
        fi
    else
        print_status warning "Nginx 未运行,无需停止。"
    fi
}

# 重启 Nginx
restart_nginx() {
    stop_nginx
    start_nginx
}

# 检查 Nginx 状态
status_nginx() {
    if is_running; then
        print_status success "Nginx 正在运行 (PID: $(cat "$NGINX_PID_FILE"))。"
    else
        print_status error "Nginx 未运行。"
    fi
}

# 显示用法
usage() {
    echo "用法: $0 {start|stop|restart|status}"
    exit 2
}

# 主程序入口
case $1 in
    start) start_nginx ;;
    stop) stop_nginx ;;
    restart) restart_nginx ;;
    status) status_nginx ;;
    *) usage ;;
esac

脚本说明

  1. 颜色功能

    • 使用 ANSI 转义序列定义了四种颜色:绿色(成功)、红色(错误)、黄色(警告)、蓝色(信息)。
  2. 功能实现

    • start_nginx: 启动 Nginx 服务,检查是否已运行。
    • stop_nginx: 停止 Nginx 服务,检查是否成功。
    • restart_nginx: 依次调用停止和启动函数。
    • status_nginx: 检查 Nginx 的运行状态。
  3. 状态检测

    • 根据 nginx.pid 文件和 ps 检测服务运行状态。
  4. 用法提示

    • 脚本未提供有效参数时,显示用法信息。
  5. 命令调整

    • 如果 nginx 的实际路径或运行方式不同,可以通过修改 $NGINX_CMD$NGINX_PID_FILE 变量适配。
  6. 兼容性

    • 适用于大多数支持 Bash 的 Linux 发行版。

使用示例

./nginx_service.sh start   # 启动 Nginx
./nginx_service.sh stop    # 停止 Nginx
./nginx_service.sh restart # 重启 Nginx
./nginx_service.sh status  # 检查 Nginx 状态

7.日志分析多功能脚本

123.123.132.90 - - [18/Dec/2024:20:40:06 +0800] "GET / HTTP/1.1" 200 2103 "-" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:06 +0800] "GET /static/css/chunk-libs.3dfb7769.css HTTP/1.1" 200 3568 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:06 +0800] "GET /static/css/app.7963745b.css HTTP/1.1" 200 263442 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:06 +0800] "GET /static/js/chunk-libs.a519c9f8.js HTTP/1.1" 200 278469 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:08 +0800] "GET /static/js/app.11993d37.js HTTP/1.1" 200 199026 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:17 +0800] "GET /static/js/chunk-elementUI.c1fcb5b4.js HTTP/1.1" 200 774050 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:18 +0800] "GET /static/js/chunk-25805cc4.ce91a669.js HTTP/1.1" 200 95585 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:18 +0800] "GET /static/css/chunk-5df68752.3fb1aada.css HTTP/1.1" 200 679 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
123.123.132.90 - - [18/Dec/2024:20:40:18 +0800] "GET /static/js/chunk-5df68752.b997e269.js HTTP/1.1" 200 5652 "http://yuchaoit.cn/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36 EdgA/131.0.0.0"
45.140.17.52 - - [18/Dec/2024:20:40:26 +0800] "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 150 "-" "-"
需求
# 按要求分析nginx的日志



# 打印功能选择菜单

1. 显示当前机器信息
2. 查询pv,uv  ,page view,访问者 记录,
# pv 刷新一次记录一次
#  uv,根据你
3. 显示访问量最高的ip,以及访问次数
4. 显示访问最频繁的业务url,最频繁的页面
5. 显示各种搜索引擎爬虫访问本站的次数
6. 显示都有哪些客户端访问了本网站

提示

nginx作为优秀的网站服务器,通过日志提取用户访问行为是最合适的了

pv,page view,表示页面浏览量,点击量,用户刷新一次,就是一个pv,也就是一个请求,一次页面浏览,因此只作为网站的一个总览访问总体访问量(基于请求方法字段提取pv)

uv ,表示unique visitor,指的是同一个客户端发出的请求,只被计算一次,基于去重后的客户端ip作为独立访客。(基于remote_addr提取 uv)

脚本

#!/bin/bash

nginx_file=$1
echo -e "------------
------日志分析系统,功能菜单------
1. 显示当前机器信息
2. 查询pv,uv
3. 显示访问量最高的10个ip,以及访问次数
4. 显示访问最频繁的10个业务url,最频繁的页面
5. 显示各种搜索引擎爬虫访问本站的次数
6. 显示都有哪些客户端访问了本网站
-------------"

read -p "请输入您的选择:" num

case $num in 
1)
    echo -e "===========当前机器信息=======
    服务器名:$(hostname)
    服务器IP: $(hostname -I
    # 服务eth0地址: $(xxx)
    # 
    当前系统时间:$(date +%T_%F)
    当前登录用户:$USER
    =========="
    ;;
2)
    echo -e "=========当前机器pv、uv统计数据======
        pv页面访问总量:$(wc -l  $nginx_file)
        ========================================================================
        uv独立访客数量:$(awk '{print $1}' $nginx_file |sort |uniq -c |wc -l)
    "
    ;;
3)
    echo -e "=========访问量最高的10个IP,访问次数==============
    $(awk '{print $1}' $nginx_file |sort |uniq -c |sort -n -r |head )"
    ;;

4)
    echo -e "=======访问量最高的10个业务url,最频繁的页面=====
    $(awk '{print $7}' $nginx_file | sort | uniq -c |sort -rn |head -10)"
    ;;
5)
    echo -e "======显示各种搜索引擎爬虫访问本站的次数========
    百度爬虫访问次数:$(grep -Ei 'baiduspider' $nginx_file |wc -l)
    必应爬虫访问次数:$(grep -Ei 'bingbot' $nginx_file |wc -l)
    谷歌爬虫访问次数:$(grep -Ei 'googlebot' $nginx_file |wc -l)
    搜狗爬虫访问次数:$(grep -Ei 'sogou web spider*' $nginx_file |wc -l)
    易搜爬虫访问次数:$( grep -Ei 'yisou' $nginx_file |wc -l)
    "
    ;;
6)
    echo -e "========访问本网站的客户端种前10种是:==============
    $( awk '{print $12}' $nginx_file|sort |uniq -c |sort -rn |head -10)"
    ;;

*)
    echo "请按要求输入选项!!!谢谢!"
    ;;
esac

参考答案-1

以下是一个可以分析 Nginx 日志并提供指定功能的 Bash 脚本:

脚本代码

#!/bin/bash

# Nginx 日志文件路径
LOG_FILE="/var/log/nginx/access.log"

# 检查日志文件是否存在
if [[ ! -f $LOG_FILE ]]; then
    echo "日志文件不存在,请检查路径: $LOG_FILE"
    exit 1
fi

# 显示当前机器信息
show_system_info() {
    echo "===== 当前机器信息 ====="
    echo "主机名: $(hostname)"
    echo "操作系统: $(uname -a)"
    echo "IP 地址: $(hostname -I | awk '{print $1}')"
    echo "当前时间: $(date)"
    echo "========================"
}

# 查询 PV 和 UV
query_pv_uv() {
    echo "===== 查询 PV 和 UV ====="
    PV=$(wc -l < "$LOG_FILE")
    UV=$(awk '{print $1}' "$LOG_FILE" | sort | uniq | wc -l)
    echo "总 PV(页面访问量): $PV"
    echo "总 UV(独立访客数): $UV"
    echo "========================"
}

# 显示访问量最高的 IP 和访问次数
top_ip() {
    echo "===== 访问量最高的 IP ====="
    awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -1 | awk '{print "IP:", $2, "访问次数:", $1}'
    echo "==========================="
}

# 显示访问最频繁的业务 URL 和页面
top_url() {
    echo "===== 访问最频繁的 URL ====="
    awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -1 | awk '{print "URL:", $2, "访问次数:", $1}'
    echo "============================"
}

# 显示搜索引擎爬虫访问次数
crawler_count() {
    echo "===== 搜索引擎爬虫访问统计 ====="
    grep -E "Googlebot|Bingbot|Baiduspider|YandexBot|Sogou" "$LOG_FILE" | awk '{print $12}' | sort | uniq -c | sort -rn
    echo "================================"
}

# 显示所有客户端
show_clients() {
    echo "===== 客户端信息 ====="
    awk -F\" '{print $6}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
    echo "======================"
}

# 打印菜单
show_menu() {
    echo "===== Nginx 日志分析菜单 ====="
    echo "1. 显示当前机器信息"
    echo "2. 查询 PV 和 UV"
    echo "3. 显示访问量最高的 IP 和访问次数"
    echo "4. 显示访问最频繁的 URL 和页面"
    echo "5. 显示搜索引擎爬虫访问本站的次数"
    echo "6. 显示都有哪些客户端访问了本网站"
    echo "0. 退出"
    echo "============================="
    echo -n "请选择功能: "
}

# 主程序入口
while true; do
    show_menu
    read -r choice
    case $choice in
        1) show_system_info ;;
        2) query_pv_uv ;;
        3) top_ip ;;
        4) top_url ;;
        5) crawler_count ;;
        6) show_clients ;;
        0) echo "退出程序"; exit 0 ;;
        *) echo "无效选项,请重新选择。" ;;
    esac
done

脚本说明

  1. 日志路径

    • 默认路径为 /var/log/nginx/access.log,可以根据需要修改变量 LOG_FILE
  2. 功能说明

    • 功能 1: 使用系统命令显示当前主机信息。
    • 功能 2: 利用 wcawk 分别统计 PV(总行数)和 UV(独立 IP)。
    • 功能 3: 使用 awksort 找出访问次数最多的 IP。
    • 功能 4: 找出访问最频繁的 URL。
    • 功能 5: 使用正则匹配统计常见搜索引擎爬虫的访问次数。
    • 功能 6: 显示所有客户端信息,包括浏览器和操作系统。
  3. 交互式菜单

    • 用户通过输入数字选择功能,选项无效时会提示重新输入。
  4. 兼容性

    • 脚本基于标准 Linux 工具开发,适用于大多数发行版。
  5. 性能优化

    • 使用 sortuniq -c 提升统计性能。

使用方式

  1. 将脚本保存为 nginx_log_analysis.sh
  2. 确保脚本可执行:
    chmod +x nginx_log_analysis.sh
    
  3. 执行脚本:
    ./nginx_log_analysis.sh
    

参考答案-2

以下是一个实现上述功能的 Bash 脚本的开发思路及代码示例:

开发思路

  1. 打印功能选择菜单:使用 echo 命令打印出用户可以选择的功能菜单。
  2. 根据用户选择执行相应操作:使用 read 命令读取用户的选择,然后使用 case 语句根据不同的选择执行不同的功能。
  3. 显示当前机器信息:使用 uname 命令获取操作系统信息,hostname 命令获取主机名,uptime 命令获取系统运行时间,free -m 命令获取内存信息等。
  4. 查询 PV 和 UV:需要解析 Nginx 的访问日志文件,统计不同 IP 的数量(UV)和日志行数(PV)。
  5. 显示访问量最高的 IP 及访问次数:使用 awksortuniq 等工具统计 Nginx 日志文件中每个 IP 出现的次数,并找出最高的。
  6. 显示访问最频繁的业务 URL 和页面:通过解析 Nginx 日志文件,找出访问最频繁的 URL。
  7. 显示搜索引擎爬虫访问次数:通过解析 Nginx 日志文件,识别出搜索引擎爬虫的用户代理(User-Agent),并统计其访问次数。
  8. 显示访问网站的客户端:通过解析 Nginx 日志文件,找出不同的客户端信息。
#!/bin/bash

# 假设 Nginx 日志文件的路径,根据实际情况修改
LOG_FILE="/var/log/nginx/access.log"

# 打印功能选择菜单
print_menu() {
    echo "请选择功能:"
    echo "1. 显示当前机器信息"
    echo "2. 查询 PV, UV"
    echo "3. 显示访问量最高的 IP,以及访问次数"
    echo "4. 显示访问最频繁的业务 URL,最频繁的页面"
    echo "5. 显示各种搜索引擎爬虫访问本站的次数"
    echo "6. 显示都有哪些客户端访问了本网站"
    echo "q. 退出"
}

# 显示当前机器信息
show_machine_info() {
    echo "操作系统:$(uname -a)"
    echo "主机名:$(hostname)"
    echo "系统运行时间:$(uptime)"
    echo "内存使用情况:"
    free -m
}

# 查询 PV 和 UV
show_pv_uv() {
    # PV 是日志文件的行数
    pv=$(wc -l < $LOG_FILE)
    # UV 是不同 IP 的数量
    uv=$(awk '{print $1}' $LOG_FILE | sort | uniq | wc -l)
    echo "PV: $pv"
    echo "UV: $uv"
}

# 显示访问量最高的 IP 及访问次数
show_top_ip() {
    # 使用 awk 统计每个 IP 的访问次数,并找出最高的
    top_ip=$(awk '{print $1}' $LOG_FILE | sort | uniq -c | sort -nr | head -n 1)
    echo "访问量最高的 IP 及访问次数:"
    echo "$top_ip"
}

# 显示访问最频繁的业务 URL 和页面
show_top_url() {
    # 使用 awk 统计每个 URL 的访问次数,并找出最高的
    top_url=$(awk '{print $7}' $LOG_FILE | sort | uniq -c | sort -nr | head -n 1)
    echo "访问最频繁的业务 URL 及访问次数:"
    echo "$top_url"
}

# 显示搜索引擎爬虫访问次数
show_crawler_count() {
    # 识别常见搜索引擎爬虫的 User-Agent,并统计其访问次数
    crawler_count=$(awk 'tolower($(NF-1)) ~ /googlebot|bingbot|yandexbot|baiduspider|sogou|360spider|yahoo! slurp/ {print $(NF-1)}' $LOG_FILE | sort | uniq -c)
    echo "搜索引擎爬虫访问次数:"
    echo "$crawler_count"
}

# 显示访问网站的客户端
show_clients() {
    # 找出不同的客户端信息
    clients=$(awk '{print $(NF-1)}' $LOG_FILE | sort | uniq)
    echo "访问网站的客户端:"
    echo "$clients"
}

while true; do
    print_menu
    read -p "请输入选项: " choice
    case $choice in
        1)
            show_machine_info
            ;;
        2)
            show_pv_uv
            ;;
        3)
            show_top_ip
            ;;
        4)
            show_top_url
            ;;
        5)
            show_crawler_count
            ;;
        6)
            show_clients
            ;;
        q)
            echo "退出程序。"
            exit 0
            ;;
        *)
            echo "无效的选项,请重新输入。"
            ;;
    esac
done

代码解释

  • 变量定义
    • LOG_FILE:定义了 Nginx 日志文件的路径,可根据实际情况修改。
  • 函数定义
    • print_menu:打印用户可以选择的功能菜单。
    • show_machine_info:使用 unamehostnameuptimefree 等命令显示当前机器信息。
    • show_pv_uv:使用 wc -l 统计日志文件的行数(PV),使用 awksortuniq 等工具统计不同 IP 的数量(UV)。
    • show_top_ip:使用 awk 提取 IP 地址,通过 sortuniq -csort -nr 找出访问量最高的 IP 及其访问次数。
    • show_top_url:使用 awk 提取 URL,通过 sortuniq -csort -nr 找出访问最频繁的 URL 及其访问次数。
    • show_crawler_count:使用 awk 提取用户代理(User-Agent),筛选出常见搜索引擎爬虫的 User-Agent,并统计其访问次数。
    • show_clients:使用 awk 提取用户代理(User-Agent),并找出不同的客户端信息。
  • 主程序部分
    • while true:创建一个无限循环,不断打印菜单和读取用户输入。
    • read -p "请输入选项: " choice:读取用户的选择。
    • case $choice in...:根据用户的选择执行相应的功能,使用 case 语句处理不同的操作,对于无效的选择,提示用户重新输入,对于 q 选项,退出程序。

使用方法

  1. 将上述脚本保存为一个文件,例如 nginx_log_analysis.sh
  2. 给文件添加可执行权限:chmod +x nginx_log_analysis.sh
  3. 运行脚本:./nginx_log_analysis.sh
  4. 根据菜单提示选择相应的功能。

请注意,上述脚本假设 Nginx 日志文件的格式为标准的 Nginx 访问日志格式,不同的日志格式可能需要调整 awk 命令的字段位置。此外,脚本中使用了一些基本的文本处理工具(如 awksortuniq 等),对于复杂的日志分析需求,可能需要更复杂的正则表达式和文本处理逻辑。你可以根据实际的日志文件和需求进一步优化和扩展脚本。

8.阅读同事写脚本

在工作里,阅读公司现有的脚本是常事,学会添加注释,理解脚本作用,然后可以开始维护脚本,维护项目。

上班之后,一般的工作技巧是

  1. 阅读ansible配置文件,主机清单文件,roles或者playbook,整体了解公司运维部署架构
  2. 然后再一层层的细看,从ansible的剧本,拆解,每一个组件的细节,涉及的配置文件,shell脚本
  3. 然后再从细节,去理解配置文件的功能,shell的细节。
  4. 自己写好总结文档,学习笔记,可以随时拿出来看。

这里是于超老师从jumpserver最新版的github代码库下载一个sh脚本,如果现在你就是这家飞致云新入职员工,你就得去阅读,维护该堡垒机的所有发布脚本。

来,试着给如下脚本,加上注释,目标如下

1.学习生产环境下其他工程师写的脚本写法

2.阅读,加注释,理解他人的脚本。

#!/bin/bash

if grep -q 'source /opt/autoenv/activate.sh' ~/.bashrc; then
    echo -e "\033[31m 正在自动载入 python 环境 \033[0m"
else
    echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m"
    exit 0
fi

source ~/.bashrc

cd `dirname $0`/ && cd .. && ./jms stop

jumpserver_backup=/tmp/jumpserver_backup$(date -d "today" +"%Y%m%d_%H%M%S")
mkdir -p $jumpserver_backup
cp -r ./* $jumpserver_backup

echo -e "\033[31m 是否需要备份Jumpserver数据库 \033[0m"
stty erase ^H
read -p "确认备份请按Y,否则按其他键跳过备份 " a
if [ "$a" == y -o "$a" == Y ];then
    echo -e "\033[31m 正在备份数据库 \033[0m"
    echo -e "\033[31m 请手动输入数据库信息 \033[0m"
    read -p '请输入Jumpserver数据库ip:' DB_HOST
    read -p '请输入Jumpserver数据库端口:' DB_PORT
    read -p '请输入Jumpserver数据库名称:' DB_NAME
    read -p '请输入有权限导出数据库的用户:' DB_USER
    read -p '请输入该用户的密码:' DB_PASSWORD
    mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || {
        echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m"
        exit 1
    }
    echo -e "\033[31m 备份数据库完成 \033[0m"
else
    echo -e "\033[31m 已取消备份数据库操作 \033[0m"
fi

git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh

cd .. && ./jms start all -d
echo -e "\033[31m 请检查jumpserver是否启动成功 \033[0m"
echo -e "\033[31m 备份文件存放于$jumpserver_backup目录 \033[0m"
stty erase ^?

exit 0
Copyright © www.yuchaoit.cn 2025 all right reserved,powered by Gitbook作者:于超 2025-01-23 19:02:40

results matching ""

    No results matching ""