shell学习从入门到精通(第二部分)

核心语法
1. 条件判断 (if-elif-else)

让脚本根据不同条件执行不同操作。

  • 基本结构:

    复制代码
    if [ condition ]; then
        # command to execute if condition is true
    elif [ another_condition ]; then
        # command to execute if another_condition is true
    else
        # command to execute if all conditions are false
    fi

    关键点:

    if 和 [ 之间必须有空格。

    和 condition 之间以及 condition 和 \] 之间也必须有空格。 then 必须单独一行,或者用分号 ; 与 if 语句放在同一行:if \[ condition \]; then ... 推荐使用 \[\[ ... \]\],它更强大且不易出错,尤其是在处理字符串时。 #!/bin/bash read -p "Enter a number: " NUM if [[ $NUM -gt 100 ]]; then echo "$NUM is greater than 100." elif [[ $NUM -eq 100 ]]; then echo "$NUM is exactly 100." else echo "$NUM is less than 100." fi

操作符 描述
== 等于
!= 不等于
-z 字符串为空
-n 字符串不为空
复制代码
#!/bin/bash
 
read -p "Enter 'yes' or 'no': " ANSWER
 
if [[ $ANSWER == "yes" ]]; then
    echo "You chose 'yes'."
elif [[ $ANSWER != "no" ]]; then
    echo "Invalid input."
else
    echo "You chose 'no'."
fi
2. case 语句

当有多个分支选择时,case 是比 if-elif-else 更清晰的替代方案。

复制代码
#!/bin/bash
 
read -p "Enter a character (a, b, or c): " CHAR
 
case $CHAR in
    a|A)
        echo "You entered 'a'."
        ;;
    b|B)
        echo "You entered 'b'."
        ;;
    c|C)
        echo "You entered 'c'."
        ;;
    *)
        echo "Invalid character."
        ;;
esac
  • 关键点:

    • | 用于匹配多个模式。

    • *) 是一个通配符,匹配任何其他输入。

    • 每个分支以 ;; 结束。

    • 整个 case 语句以 esac (case反写) 结束。

3. 循环结构
  • for 循环:

  • 遍历列表:

    复制代码
    #!/bin/bash
     
    for FRUIT in apple banana orange; do
        echo "I like $FRUIT."
    done

    遍历数字序列:

    复制代码
    #!/bin/bash
     
    echo "Counting from 1 to 5:"
    for i in {1..5}; do
        echo $i
    done

    C 语言风格的 for 循环:

    复制代码
    #!/bin/bash
     
    for (( i=0; i<5; i++ )); do
        echo "C-style loop, iteration: $i"
    done

while 循环: 当条件为真时持续循环。常用于逐行读取文件。

复制代码
#!/bin/bash
 
COUNTER=0
while [[ $COUNTER -lt 5 ]]; do
    echo "Counter is $COUNTER"
    # 必须有改变条件的语句,否则会死循环
    let COUNTER++ 
done

逐行读取文件(重要用法):

复制代码
#!/bin/bash
 
FILENAME="hello.sh"
while IFS= read -r line; do
    echo "Line: $line"
done < "$FILENAME"
    • IFS= read -r line 是读取文件的标准、安全的方式,可以防止 read 命令意外地处理反斜杠和行首行尾的空白字符。
  • until 循环: 当条件为假时持续循环,直到条件为真。

    复制代码
    #!/bin/bash
     
    COUNTER=0
    until [[ $COUNTER -ge 5 ]]; do
        echo "Counter is $COUNTER"
        let COUNTER++
    done
4. 函数 (Functions)

将代码块封装成函数,方便复用。

  • 定义和调用:

    复制代码
    #!/bin/bash
     
    # 定义函数
    greet() {
        echo "Hello there!"
    }
     
    # 调用函数
    echo "Calling the function..."
    greet
    echo "Function called."

    传递参数:

  • 在函数内部,$1, $2, $3, ... 分别代表第一个、第二个、第三个参数。

  • $@ 代表所有参数的列表。

  • $# 代表传递给函数的参数个数。

    复制代码
    #!/bin/bash
     
    print_info() {
        if [[ $# -eq 0 ]]; then
            echo "Usage: print_info <name> <age>"
            return 1 # 返回一个非零值表示错误
        fi
     
        echo "Name: $1"
        echo "Age: $2"
        echo "All arguments: $@"
    }
     
    print_info "Bob" 42
    print_info # 测试错误处理

    返回值:

    Shell 函数的 return 语句只返回一个 0-255 的整数,称为 退出状态码。0 通常表示成功,非 0 表示失败。

    要 "返回" 数据,通常是在函数中用 echo 输出,然后在调用处用命令替换 $(...) 来捕获输出。

    local 关键字使变量的作用域仅限于函数内部,这是一个好习惯。

    #!/bin/bash

    get_full_name() {

    local first_name=$1

    local last_name=$2

    使用 echo "返回" 结果

    echo "first_name last_name"

    }

    使用命令替换捕获函数的输出

    FULL_NAME=$(get_full_name "John" "Doe")

    echo "The full name is: $FULL_NAME"

  • 第三部分:高级用法

    1. 数组 (Arrays)

    索引数组 (Indexed Arrays):

    #!/bin/bash

    定义数组

    fruits=("Apple" "Banana" "Cherry")

    访问元素(索引从0开始)

    echo "First fruit: ${fruits[0]}"

    访问所有元素

    echo "All fruits: ${fruits[@]}"

    获取数组长度

    echo "Number of fruits: ${#fruits[@]}"

    添加元素

    fruits+=("Orange")

    遍历数组

    for fruit in "${fruits[@]}"; do

    echo "Processing $fruit"

    done

    AI写代码

    关联数组 (Associative Arrays / Hashes): 键值对数组(需要 Bash 4.0+)。

    #!/bin/bash

    声明一个关联数组

    declare -A user

    赋值

    user["name"]="Alice"

    user["id"]="101"

    user["email"]="alice@example.com"

    访问元素

    echo "User Name: ${user[name]}"

    遍历所有的键

    echo "All keys: ${!user[@]}"

    遍历所有的值

    echo "All values: ${user[@]}"

    遍历键值对

    for key in "${!user[@]}"; do

    echo "key: {user[$key]}"

    done

    AI写代码

    1. 字符串处理

    操作 示例 结果

    长度 ${#str} 字符串长度

    截取 ${str:2:3} 从索引 2 开始取 3 个字符

    替换 ${str/old/new} 替换第一个 old 为 new

    全替换 ${str//old/new} 替换所有 old 为 new

    删除前缀 ${str#prefix} 删除最短前缀

    删除后缀 ${str%suffix} 删除最短后缀

    示例:

    #!/bin/bash

    str="hello world"

    echo "长度:${#str}" # 输出:11

    echo "截取:${str:3:4}" # 输出:lo w

    echo "替换:${str/world/shell}" # 输出:hello shell

    AI写代码

    1. 精细数学计算

    Shell 内置的 $((...)) 只支持整数运算。对于浮点数或更复杂的计算,需要借助外部工具。

    整数计算:

    #!/bin/bash

    A=10

    B=3

    SUM=$((A + B))

    PRODUCT=$((A * B))

    REMAINDER=$((A % B)) # 取余

    echo "Sum: SUM, Product: PRODUCT, Remainder: $REMAINDER"

    AI写代码

    浮点数计算 (bc): bc 是一个强大的计算器,-l 参数可以加载数学库,支持高精度计算。

    #!/bin/bash

    将表达式通过管道传给 bc

    RESULT=$(echo "scale=4; 10 / 3" | bc)

    echo "10 / 3 = $RESULT"

    更复杂的计算

    PI=$(echo "scale=10; 4*a(1)" | bc -l) # a() 是反正切函数,4*a(1)是计算pi的经典方法

    echo "Pi ≈ $PI"

    VAR1=5.5

    VAR2=2.2

    SUM=(echo "VAR1 + $VAR2" | bc)

    echo "VAR1 + VAR2 = $SUM"

    AI写代码

    使用 awk 计算: awk 也是一个处理文本和进行计算的强大工具。

    #!/bin/bash

    RESULT=$(awk "BEGIN {printf \"%.4f\", 10/3}")

    echo "10 / 3 = $RESULT"

    AI写代码

    1. 颜色和格式化输出

    通过 ANSI 转义序列设置输出颜色,格式:\033[颜色代码m文本\033[0m(0m 重置颜色)

    常用颜色代码:

    文本色:30(黑)、31(红)、32(绿)、33(黄)、34(蓝)、35(紫)、36(青)、37(白)

    背景色:40(黑)、41(红)、42(绿)等

    使用 ANSI escape codes 来控制终端输出的颜色和样式。

    语法: \e[...m 或 \033[...m

    示例:

    #!/bin/bash

    为了可读性和复用,最好将颜色代码定义为变量

    COLOR_RESET='\e[0m'

    COLOR_RED='\e[31m'

    COLOR_GREEN='\e[32m'

    COLOR_YELLOW='\e[33m'

    BG_BLUE='\e[44m'

    STYLE_BOLD='\e[1m'

    echo -e "{COLOR_RED}This is red text.{COLOR_RESET}"

    echo -e "{COLOR_GREEN}This is green text.{COLOR_RESET}"

    echo -e "{STYLE_BOLD}{COLOR_YELLOW}This is bold yellow text.${COLOR_RESET}"

    echo -e "{BG_BLUE}This text has a blue background.{COLOR_RESET}"

    组合使用

    echo -e "{STYLE_BOLD}{COLOR_RED}{BG_BLUE}DANGER! Critical Error!{COLOR_RESET}"

    AI写代码

    echo -e 是必须的,它让 echo 能够解释转义序列。

    1. 强大的命令执行与控制

    命令分组:

    ( ... ): 在一个 子 Shell 中执行命令组。子 Shell 中的变量和目录改变不会影响父 Shell。

    { ...; }: 在 当前 Shell 中执行命令组。注意 } 前必须有分号或换行。

    子Shell示例

    echo "Before: PWD=$PWD"

    (cd /tmp; echo "Inside subshell: PWD=$PWD")

    echo "After: PWD=$PWD" # PWD 没变

    当前Shell示例

    echo "Before: PWD=$PWD"

    { cd /var; echo "Inside group: PWD=$PWD"; }

    echo "After: PWD=$PWD" # PWD 变了

    AI写代码

    输入/输出重定向:

    >: 重定向标准输出(会覆盖文件内容)。 ls > file.txt

    >>: 重定向标准输出(追加到文件末尾)。date >> file.txt

    <: 重定向标准输入。 read -r line < file.txt

    2>: 重定向标准错误。command_that_fails 2> error.log

    &>: 重定向标准输出和标准错误。command &> all_output.log

    /dev/null: 一个特殊的设备文件,所有写入它的数据都会被丢弃("黑洞")。常用于丢弃不想要的输出。command > /dev/null 2>&1

    管道 (|): 将前一个命令的标准输出作为后一个命令的标准输入。这是 Shell 的精髓之一。

    统计当前目录有多少个 .sh 文件

    ls -l | grep ".sh$" | wc -l

    AI写代码

    进程替换 (<(command)): 这是一个非常高级的特性,它将一个命令的输出伪装成一个文件,然后可以被另一个需要文件作为输入的命令使用。

    比较两个目录下的文件列表,而无需创建临时文件

    diff <(ls /bin) <(ls /usr/bin)

    AI写代码

    Here 文档(<<)

    向命令输入多行文本(无需手动输入),语法

    命令 << 分界符

    多行文本

    分界符

    AI写代码

    #!/bin/bash

    向文件写入多行内容

    cat << EOF > info.txt

    Name: Alice

    Age: 25

    City: Beijing

    EOF

    info.txt 内容为上述三行

    作为函数输入

    count_lines() {

    wc -l

    }

    count_lines << EOF

    line1

    line2

    line3

    EOF

    输出:3(行数)

    AI写代码

    第四部分:编写健壮的脚本

    1. set 命令

    在脚本开头使用 set 命令可以使其更安全、更健壮。

    #!/bin/bash

    set -euo pipefail

    AI写代码

    set -e: 脚本中任何命令失败(返回非零退出状态码)时,立即退出。

    set -u: 尝试使用未定义的变量时,立即退出。

    set -o pipefail: 在管道中,只要有任何一个命令失败,整个管道的退出状态码就是失败的。

    1. 解析脚本选项 (getopts)

    用于解析传递给脚本的命令行选项(如 -f, -v)。

    #!/bin/bash

    set -euo pipefail

    VERBOSE=false

    FILENAME=""

    f: 表示-f选项需要一个参数

    v 表示-v选项不需要参数

    while getopts 'vf:' OPTION; do

    case "$OPTION" in

    v)

    VERBOSE=true

    ;;

    f)

    FILENAME="$OPTARG"

    ;;

    ?)

    echo "Usage: (basename 0) [-v] [-f filename]"

    exit 1

    ;;

    esac

    done

    if [[ $VERBOSE == true ]]; then

    echo "Verbose mode is ON."

    fi

    if [[ -n "$FILENAME" ]]; then

    echo "Processing file: $FILENAME"

    else

    echo "No filename provided."

    fi

    AI写代码

    执行示例:

    ./myscript.sh -v -f data.txt

    ./myscript.sh -f report.csv

    1. 信号陷阱 (trap)

    允许你在脚本接收到特定信号(如 Ctrl+C)时执行一段代码,常用于清理临时文件。

    #!/bin/bash

    set -euo pipefail

    创建一个临时文件

    TMP_FILE=$(mktemp)

    echo "Created temporary file: $TMP_FILE"

    定义清理函数

    cleanup() {

    echo "Caught signal! Cleaning up..."

    rm -f "$TMP_FILE"

    echo "Cleanup finished."

    exit 1

    }

    设置陷阱:当接收到 INT(Ctrl+C) 或 TERM 信号时,执行 cleanup 函数

    trap cleanup INT TERM

    主逻辑

    echo "Script is running, press Ctrl+C to test the trap."

    sleep 60 # 模拟长时间运行的任务

    echo "Script finished normally."

    正常退出前也要清理

    rm -f "$TMP_FILE"

    AI写代码

    捕获系统信号(如 Ctrl+C 发送的 SIGINT),执行自定义操作。

    #!/bin/bash

    捕获 SIGINT 信号(Ctrl+C)

    trap 'echo " 不要按 Ctrl+C!"; exit 1' SIGINT

    echo "运行中(按 Ctrl+C 测试)..."

    while true; do

    sleep 1

    done

    AI写代码

    1. 正则表达式与文本处理

    Shell 结合 grep(搜索)、sed(编辑)、awk(分析)可强大处理文本。

    1. grep:文本搜索

    语法:grep [选项] 模式 文件

    常用选项:-i(忽略大小写)、-v(反向匹配)、-n(显示行号)、-E(扩展正则)。

    #!/bin/bash

    在文件中搜索包含 "error" 的行(区分大小写)

    grep "error" log.txt

    忽略大小写搜索,显示行号

    grep -in "warning" log.txt

    反向匹配(不包含 "debug" 的行)

    grep -v "debug" log.txt

    扩展正则(-E),匹配 "apple" 或 "banana"

    grep -E "apple|banana" fruits.txt

    AI写代码

    1. sed:文本替换与编辑

    语法:sed [选项] '命令' 文件

    常用命令:s/原字符串/新字符串/(替换,默认替换每行第一个匹配)、s/.../.../g(全局替换)。

    #!/bin/bash

    替换文件中 "old" 为 "new"(仅输出,不修改原文件)

    sed 's/old/new/' text.txt

    全局替换并修改原文件(-i 选项,备份用 -i.bak)

    sed -i 's/hello/HELLO/g' greet.txt # 所有 hello 替换为 HELLO

    删除空行(d 命令删除匹配行)

    sed '/^/d' input.txt # \^ 匹配空行

    AI写代码

    1. awk:文本分析与处理

    擅长按列处理文本(默认空格分隔),语法:awk '模式 {动作}' 文件。

    #!/bin/bash

    打印文件第2列和第4列

    awk '{print 2, 4}' data.txt

    按条件过滤(第3列数值 > 100 的行)

    awk '3 \> 100 {print 0}' data.txt # $0 表示整行

    自定义分隔符(-F 选项),按逗号分隔,打印第1列

    awk -F ',' '{print $1}' csvfile.txt

    AI写代码

    1. 进程管理

    Shell 可启动、查看、终止进程。

    1. 后台运行与 jobs

    &:在命令后加 & 使其后台运行。

    jobs:查看当前终端的后台进程。

    fg %n:将第 n 个后台进程调回前台。

    bg %n:将暂停的后台进程继续运行。

    #!/bin/bash

    后台运行长时间任务(输出重定向到文件)

    sleep 30 > sleep.log 2>&1 &

    echo "后台进程 ID:!" # ! 是后台进程 PID

    查看后台进程

    jobs

    终止进程(kill)

    pid=$!

    kill $pid # 发送 SIGTERM 信号

    kill -9 $pid # 强制终止(SIGKILL,无法捕获)

    AI写代码

    1. 进程替换(<(命令)、>(命令))

    将命令输出作为临时文件,用于需要文件参数的命令。

    #!/bin/bash

    比较两个命令的输出(无需临时文件)

    diff <(ls dir1) <(ls dir2) # 比较 dir1 和 dir2 的文件列表

    将输出同时发送到终端和文件(tee 命令)

    ls -l | tee >(grep ".sh" > sh_files.txt) # 筛选 .sh 文件到 sh_files.txt,同时显示所有

    AI写代码

    1. 调试脚本

    bash -n 脚本:检查语法错误(不执行)。

    bash -x 脚本:执行并输出每一行命令(调试细节)。

    在脚本中用 set -x(开启调试)、set +x(关闭)。

    #!/bin/bash

    set -x # 开启调试

    a=5

    b=3

    echo $((a + b))

    set +x # 关闭调试

    echo "调试结束"

    AI写代码

    第五部分:综合脚本示例

    案例 1:批量重命名文件

    #!/bin/bash

    功能:将当前目录所有 .txt 文件重命名为 "prefix_数字.txt"(如 prefix_1.txt)

    count=1

    for file in *.txt; do

    跳过非文件(如目录)

    if [ ! -f "$file" ]; then

    continue

    fi

    重命名

    mv "file" "prefix_{count}.txt"

    echo "重命名:file → prefix_{count}.txt"

    ((count++))

    done

    AI写代码

    案例 2:系统监控脚本

    #!/bin/bash

    功能:监控系统 CPU、内存、磁盘使用率,超过阈值则报警

    阈值(百分比)

    CPU_THRESHOLD=80

    MEM_THRESHOLD=80

    DISK_THRESHOLD=90

    获取 CPU 使用率(取整数)

    cpu_usage=(top -bn1 \| grep "Cpu(s)" \| awk '{print 2 + $4}' | cut -d. -f1)

    获取内存使用率

    mem_usage=(free \| grep Mem \| awk '{print 3/$2 * 100}' | cut -d. -f1)

    获取磁盘使用率(根目录)

    disk_usage=(df -h / \| grep / \| awk '{print 5}' | sed 's/%//')

    检查 CPU

    if (( cpu_usage > CPU_THRESHOLD )); then

    echo "报警:CPU 使用率过高($cpu_usage%)"

    fi

    检查内存

    if (( mem_usage > MEM_THRESHOLD )); then

    echo "报警:内存使用率过高($mem_usage%)"

    fi

    检查磁盘

    if (( disk_usage > DISK_THRESHOLD )); then

    echo "报警:磁盘使用率过高($disk_usage%)"

    fi

    AI写代码

    总结

    掌握 Shell 脚本是一个循序渐进的过程。

    入门: 从 echo、变量、read 和简单的 if 开始,学会编写和执行基础脚本。

    进阶: 熟练运用循环、函数、case 语句和各种条件判断,能够编写逻辑复杂的脚本。

    高级: 掌握数组、bc 计算、颜色输出、进程替换、getopts 和 trap 等高级特性,编写出功能强大、交互友好且非常健壮的专业脚本。


    版权声明:本文为CSDN博主「zwjapple」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

    原文链接:https://blog.csdn.net/zwjapple/article/details/149243023