shell学习从入门到精通

bShell 语法学习:从入门到精通

Shell 是一个命令解释器,它充当用户与操作系统内核之间的接口。Shell 脚本(Shell Script)则是由一系列 Shell 命令组成的文本文件,用于自动化执行任务。最常用的 Shell 是 Bash (Bourne-Again SHell),本指南将主要以 Bash 为例。

第一部分:入门基础

1. "Hello World" - 你的第一个脚本

学习任何语言的第一步都是 "Hello World"。

  • 脚本内容 (hello.sh):

    复制代码
    #!/bin/bash
    
    # 这是一个注释。井号(#)后面的内容会被忽略。
    echo "Hello, World!"
  • 关键点解释:

    • #!/bin/bash: 这叫做 "Shebang"。它告诉系统这个脚本应该使用哪个解释器来执行,这里是 /bin/bash

    • #: 这是注释符号。

    • echo: 这是最常用的命令之一,用于在终端输出文本或变量内容。

  • 如何执行脚本:

    • 方法一:作为 bash 的参数

      复制代码
      bash hello.sh
    • 方法二:赋予执行权限后直接运行(推荐)

      1. 添加执行权限: chmod +x hello.sh

      2. 执行脚本: ./hello.sh ( ./ 表示当前目录)

2. 变量 (Variables)

变量用于存储数据。Bash 中的变量不需要事先声明类型。

  • 定义和使用:

    • 规则:变量名、等号和值之间 不能有空格

    • 引用:使用 $ 符号。推荐用双引号 "" 包裹变量,以避免空格等特殊字符引起的问题。

      #!/bin/bash

      定义变量

      NAME="Alice"
      AGE=30

      使用变量

      echo "My name is NAME, and I am AGE years old."

      推荐使用花括号{}来明确变量边界

      echo "This is ${NAME}'s book."

  • 只读变量:

    复制代码
    CITY="New York"
    readonly CITY
    # 下面这行会报错: CITY: readonly variable
    # CITY="London" 
  • 接收用户输入 (read):

    复制代码
    #!/bin/bash
    
    echo "What is your name?"
    read USER_NAME
    echo "Hello, $USER_NAME!"
    
    # -p 可以在同一行显示提示信息
    read -p "What is your favorite color? " COLOR
    echo "I see, your favorite color is $COLOR."
2.1. 变量引用
  • 语法:$变量名${变量名}(推荐后者,避免歧义)

    #!/bin/bash

    name="Alice"
    echo "Name: name" # 输出:Name: Alice echo "Name: {name}" # 输出:Name: Alice(推荐)

    变量拼接(无需连接符)

    greeting="Hello, {name}!" echo greeting # 输出:Hello, Alice!

    变量作为命令参数

    file="test.txt"
    cat ${file} # 等价于 cat test.txt

2.2. 特殊变量(预定义变量)

Shell 有一系列内置变量,用于获取脚本运行时的信息:

变量 含义 示例(执行 ./script.sh arg1 arg2
$0 脚本文件名 echo $0./script.sh
$n 第 n 个参数(n≥1) echo $1arg1echo $2arg2
$# 参数总数 echo $#2
$* 所有参数(作为单个字符串) echo $*arg1 arg2
$@ 所有参数(作为独立字符串) for i in $@; do echo $i; done → 分别输出 arg1、arg2
$? 上一条命令的退出状态(0 成功,非 0 失败) ls non_exist; echo $?2(失败)
$$ 当前脚本的进程 ID(PID) echo $$12345(当前进程 ID)
$! 上一个后台进程的 PID sleep 10 &; echo $! → 后台进程的 PID
复制代码
#!/bin/bash
# 保存为 args.sh,执行:./args.sh a b c

echo "脚本名:$0"  # 输出:脚本名:./args.sh
echo "第1个参数:$1"  # 输出:第1个参数:a
echo "参数总数:$#"  # 输出:参数总数:3
echo "所有参数(\$*):$*"  # 输出:所有参数($*):a b c

# 遍历 $@(每个参数独立处理)
echo "遍历 \$@:"
for arg in "$@"; do
  echo "- $arg"
done
# 输出:
# 遍历 $@:
# - a
# - b
# - c
2.3. 环境变量

环境变量是全局变量,用于配置系统环境(如 PATHHOME),可通过 export 声明。

复制代码
#!/bin/bash

# 查看环境变量(已存在的)
echo "HOME: $HOME"  # 输出:HOME: /home/user(当前用户家目录)
echo "PATH: $PATH"  # 输出:系统命令搜索路径

# 定义并导出环境变量(子进程可访问)
export MY_VAR="hello"
# 在当前终端执行脚本后,可通过 echo $MY_VAR 查看(仅当前终端有效)

# 临时设置环境变量(仅当前命令有效)
MY_VAR="test" echo $MY_VAR  # 输出:test
3. 命令替换

将一个命令的输出结果赋值给一个变量。

  • 语法: $(command) (推荐) 或 command (旧式)

    复制代码
    #!/bin/bash
    
    CURRENT_DATE=$(date "+%Y-%m-%d %H:%M:%S")
    FILES_IN_DIR=$(ls -l)
    
    echo "Current time is: $CURRENT_DATE"
    echo "---"
    echo "Files in the current directory:"
    echo "$FILES_IN_DIR"

4. 数据类型与扩展

Shell 没有严格的数据类型,但支持字符串、数字、数组、关联数组(字典)。

1. 字符串

Shell 字符串可用单引号 '' 或双引号 "",区别:

  • 单引号:不解析变量,不转义特殊字符(除单引号本身)。

  • 双引号:解析变量 ,可转义特殊字符(如 \n\t)。

    #!/bin/bash

    name="Alice"

    单引号(变量不解析)

    echo 'Hello, name' # 输出:Hello, name

    双引号(变量解析)

    echo "Hello, $name" # 输出:Hello, Alice

    转义字符(需双引号)

    echo "Line1\nLine2" # 输出:Line1\nLine2(默认不转义)
    echo -e "Line1\nLine2" # 加 -e 启用转义,输出:

    Line1

    Line2

    字符串长度

    str="hello"
    echo ${#str} # 输出:5

    字符串截取(${变量:起始位置:长度},起始位置从0开始)

    echo ${str:1:3} # 从索引1开始,取3个字符 → ell

5. 运算符与表达式

Shell 支持算术运算、字符串运算、逻辑运算,需用特定语法(如 $((...)))。

1. 算术运算符

语法:$((表达式))$[表达式](推荐前者),支持 +-*/%(取余)、**(幂)。

复制代码
#!/bin/bash

a=10
b=3

# 加法
echo $((a + b))  # 输出:13

# 乘法(注意:* 无需转义,在 $((...)) 中直接用)
echo $((a * b))  # 输出:30

# 取余
echo $((a % b))  # 输出:1

# 幂运算(Bash 4.0+ 支持)
echo $((2 **3))  # 输出:8

# 赋值运算(在表达式中修改变量)
$((a += 5))  # 等价于 a = a + 5
echo $a  # 输出:15
2. 比较运算符(数字)

用于条件判断(if 语句中),语法:[ 数字1 运算符 数字2 ](( 数字1 运算符 数字2 ))

运算符 含义 示例(a=10, b=5)
-eq 等于 [ $a -eq $b ] → false
-ne 不等于 [ $a -ne $b ] → true
-gt 大于 [ $a -gt $b ] → true
-lt 小于 [ $a -lt $b ] → false
-ge 大于等于 (( a >= b )) → true
-le 小于等于 (( a <= b )) → false
复制代码
#!/bin/bash

a=10
b=5

if [ $a -gt $b ]; then
  echo "$a 大于 $b"
fi
# 输出:10 大于 5

# 用 ((...)) 更简洁(支持 >、< 等符号)
if (( a > b )); then
  echo "$a 大于 $b"
fi
# 输出:10 大于 5
3. 字符串运算符

用于字符串比较,语法:[ 字符串1 运算符 字符串2 ][[ 字符串1 运算符 字符串2 ]]

运算符 含义 示例(s1="abc", s2="abd")
= 等于(== 等价) [ "$s1" = "$s2" ] → false
!= 不等于 [ "$s1" != "$s2" ] → true
-z 字符串长度为 0 [ -z "$s1" ] → false
-n 字符串长度不为 0 [ -n "$s1" ] → true
< 字典序小于(需 [[]]) [[ "$s1" < "$s2" ]] → true(abc < abd)
> 字典序大于(需 [[]]) [[ "$s1" > "$s2" ]] → false
复制代码
#!/bin/bash

s1="abc"
s2="abd"

# 字符串是否相等
if [ "$s1" = "$s2" ]; then
  echo "相等"
else
  echo "不相等"
fi
# 输出:不相等

# 字符串长度是否不为0
if [ -n "$s1" ]; then
  echo "$s1 长度不为0"
fi
# 输出:abc 长度不为0

# 字典序比较(需用 [[ ]])
if [[ "$s1" < "$s2" ]]; then
  echo "$s1 小于 $s2"
fi
# 输出:abc 小于 abd
4. 逻辑运算符

用于组合条件,支持与(-a&&)、或(-o||)、非(!)。

复制代码
#!/bin/bash

a=10
b=5
c=15

# 与运算(两个条件都成立)
if [ $a -gt $b ] && [ $a -lt $c ]; then
  echo "$a 大于 $b 且小于 $c"
fi
# 输出:10 大于 5 且小于 15

# 或运算(至少一个条件成立)
if [ $a -gt $c ] || [ $a -gt $b ]; then
  echo "$a 大于 $c 或大于 $b"
fi
# 输出:10 大于 15 或大于 5

# 非运算(条件取反)
if ! [ $a -eq $b ]; then
  echo "$a 不等于 $b"
fi
# 输出:10 不等于 5

第二部分:核心语法

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
  • 文件系统判断:

    操作符 描述
    -f 文件存在且是常规文件
    -d 文件存在且是目录
    -e 文件存在(不区分类型)
    -r 文件可读
    -w 文件可写
    -x 文件可执行
    复制代码
    #!/bin/bash
    
    FILE="./hello.sh"
    
    if [[ -f "$FILE" ]]; then
        echo "$FILE is a regular file."
        if [[ -x "$FILE" ]]; then
            echo "And it is executable."
        fi
    elif [[ -d "$FILE" ]]; then
        echo "$FILE is a directory."
    else
        echo "$FILE does not exist or is not a regular file/directory."
    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
  • 关联数组 (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

2. 字符串处理

操作 示例 结果
长度 ${#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
3. 精细数学计算

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

  • 整数计算:

    复制代码
    #!/bin/bash
    
    A=10
    B=3
    
    SUM=$((A + B))
    PRODUCT=$((A * B))
    REMAINDER=$((A % B)) # 取余
    
    echo "Sum: $SUM, Product: $PRODUCT, Remainder: $REMAINDER"
  • 浮点数计算 (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"
  • 使用 awk 计算: awk 也是一个处理文本和进行计算的强大工具。

    复制代码
    #!/bin/bash
    
    RESULT=$(awk "BEGIN {printf \"%.4f\", 10/3}")
    echo "10 / 3 = $RESULT"
4. 颜色和格式化输出

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

常用颜色代码:

  • 文本色: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}"
    • echo -e 是必须的,它让 echo 能够解释转义序列。
5. 强大的命令执行与控制
  • 命令分组:

    • ( ... ): 在一个 子 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 变了

  • 输入/输出重定向:

    • >: 重定向标准输出(会覆盖文件内容)。 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
  • 进程替换 (<(command)): 这是一个非常高级的特性,它将一个命令的输出伪装成一个文件,然后可以被另一个需要文件作为输入的命令使用。

    复制代码
    # 比较两个目录下的文件列表,而无需创建临时文件
    diff <(ls /bin) <(ls /usr/bin)

Here 文档(<<

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

复制代码
命令 << 分界符
  多行文本
分界符

#!/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(行数)

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

1. set 命令

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

复制代码
#!/bin/bash
set -euo pipefail
  • set -e: 脚本中任何命令失败(返回非零退出状态码)时,立即退出。

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

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

2. 解析脚本选项 (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
  • 执行示例:

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

    • ./myscript.sh -f report.csv

3. 信号陷阱 (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"

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

复制代码
#!/bin/bash

# 捕获 SIGINT 信号(Ctrl+C)
trap 'echo " 不要按 Ctrl+C!"; exit 1' SIGINT

echo "运行中(按 Ctrl+C 测试)..."
while true; do
  sleep 1
done

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

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

2. 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 # ^ 匹配空行

3. 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

5. 进程管理

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,无法捕获)

2. 进程替换(<(命令)>(命令)

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

复制代码
#!/bin/bash

# 比较两个命令的输出(无需临时文件)
diff <(ls dir1) <(ls dir2)  # 比较 dir1 和 dir2 的文件列表

# 将输出同时发送到终端和文件(tee 命令)
ls -l | tee >(grep ".sh" > sh_files.txt)  # 筛选 .sh 文件到 sh_files.txt,同时显示所有
6. 调试脚本
  • bash -n 脚本:检查语法错误(不执行)。

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

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

    #!/bin/bash

    set -x # 开启调试
    a=5
    b=3
    echo $((a + b))
    set +x # 关闭调试
    echo "调试结束"

第五部分:综合脚本示例

案例 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
案例 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

总结

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

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

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

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

最重要的是 多写、多练、多看。尝试用脚本去自动化你日常工作中的重复性任务,这是最好的学习方式。

相关推荐
源文雨1 天前
shell调用ffmpeg递归转换所有wav至flac的脚本
ffmpeg·bash·音视频·音频·unix·shell·音频编码
Sheep Shaun2 天前
如何让一个进程诞生、工作、终止并等待回收?——探索Linux进程控制与Shell的诞生
linux·服务器·数据结构·c++·算法·shell·进程控制
dingdingfish3 天前
Bash 学习 - 第1章:Introduction
bash·shell·programming·introduction
pr_note5 天前
legality检查
shell·tcl
啥都不懂的小小白6 天前
Shell脚本编程入门:从零基础到实战掌握
前端·shell
dingdingfish10 天前
GNU Parallel 学习 - 第1章:How to read this book
bash·shell·gnu·parallel
似霰12 天前
Linux Shell 脚本编程——核心基础语法
linux·shell
似霰13 天前
Linux Shell 脚本编程——脚本自动化基础
linux·自动化·shell
偷学技术的梁胖胖yo14 天前
Shell脚本中连接数据库查询数据报错 “No such file or directory“以及函数传参数组
linux·mysql·shell
纵有疾風起23 天前
【Linux 系统开发】基础开发工具详解:软件包管理器、编辑器。编译器开发实战
linux·服务器·开发语言·经验分享·bash·shell