【Shell编程 / 5】文件操作、进程控制与错误处理技巧

1. 文件处理

在 Shell 编程中,文件处理是一个非常重要的部分。文件操作是 Linux 系统的核心任务之一,Shell 脚本中提供了多种命令来处理和管理文件内容。掌握这些命令将帮助你更高效地处理文本文件、进行数据过滤和分析,增强脚本的功能和灵活性。


1.1 文本处理工具

文本处理工具可以用来读取文件内容、搜索数据、进行数据替换、格式化输出等。以下是常用的几个文本处理工具。

  1. grep:搜索文本中的特定模式。
    grep 用于在文件或输入流中查找匹配指定模式的行。它可以通过正则表达式进行灵活匹配。

    grep "pattern" filename  # 在文件中查找包含 "pattern" 的行
    grep -i "pattern" filename  # 忽略大小写查找
    grep -r "pattern" /dir  # 递归查找目录中的所有文件
    
  2. awk:按列处理文本文件,特别适用于处理格式化数据。
    awk 是一个文本处理工具,通常用于按行读取文件并按列分隔数据进行处理。

    awk '{print $1, $3}' filename  # 打印每行的第一列和第三列
    awk -F',' '{print $1, $2}' filename  # 使用逗号作为分隔符
    
  3. sed:流编辑器,用于替换、删除或插入文本。
    sed 允许对文件或标准输入的文本进行编辑,常用于替换、删除行或进行复杂的文本处理。

    sed 's/old/new/g' filename  # 将文件中的 "old" 替换为 "new"
    sed '/pattern/d' filename  # 删除包含 "pattern" 的行
    
  4. cut:按分隔符提取列。
    cut 用于从文件或输入流中提取指定的列,可以指定分隔符来切割每行数据。

    cut -d',' -f1 filename  # 按逗号分隔提取第一列
    cut -f2,4 filename  # 提取第2列和第4列
    
  5. sort:对文件中的数据进行排序。
    sort 用于对文件中的行进行排序,可以按照字母、数字或自定义方式排序。

    sort filename  # 默认按字典顺序排序
    sort -n filename  # 按数字顺序排序
    
  6. uniq:去除重复行。
    uniq 用于删除相邻的重复行,通常与 sort 一起使用,以确保去除所有重复行。

    uniq filename  # 删除相邻的重复行
    uniq -c filename  # 显示每行的重复次数
    

1.2 重定向和管道

在 Shell 中,重定向和管道是非常有用的工具,可以将命令的输出传递给文件或另一个命令,进而实现更复杂的数据处理。

  1. 重定向(Redirection):将命令的输出或错误信息重定向到文件。

    • >:将命令的输出重定向到文件,并覆盖文件内容。

      echo "Hello World" > output.txt  # 将 "Hello World" 写入 output.txt(覆盖)
      
    • >>:将命令的输出追加到文件末尾。

      echo "Append this line" >> output.txt  # 将内容追加到 output.txt
      
    • 2>:将标准错误输出重定向到文件。

      ls non_existent_file 2> error.log  # 将错误信息写入 error.log
      
  2. 管道(Pipe):通过管道符 | 将一个命令的输出传递给下一个命令。管道允许将多个命令串联起来,形成一个命令链。

    cat file.txt | grep "pattern"  # 查找 file.txt 中包含 "pattern" 的行
    ps aux | grep "process_name"  # 查找正在运行的进程
    

1.3 文件处理命令

Shell 提供了多种命令用于查看和操作文件内容,下面是常用的文件处理命令。

  1. cat:连接文件并显示内容。
    cat 用于查看文件内容,适用于显示小文件或将多个文件合并成一个文件。

    cat filename  # 显示文件内容
    cat file1 file2 > combined.txt  # 合并多个文件并输出到新文件
    
  2. tac:与 cat 相反,tac 按行逆序显示文件内容。

    tac filename  # 逆序显示文件内容
    
  3. head:显示文件的前几行,默认显示前 10 行。

    head filename  # 默认显示前 10 行
    head -n 20 filename  # 显示文件的前 20 行
    
  4. tail:显示文件的最后几行,默认显示最后 10 行。常用于查看日志文件的最后部分。

    tail filename  # 默认显示最后 10 行
    tail -n 20 filename  # 显示文件的最后 20 行
    tail -f filename  # 实时查看文件新增的内容(用于日志)
    
  5. wc:统计文件的行数、字数和字符数。
    wc 是一个简单的命令,用于统计文件的行数、字数和字符数,常用于文本文件的基本分析。

    wc filename  # 显示行数、字数和字符数
    wc -l filename  # 只显示行数
    wc -w filename  # 只显示字数
    

2. 进程管理

在 Linux 系统中,进程管理是非常重要的,尤其是在进行系统监控、脚本编写或开发后台服务时,理解如何管理进程能帮助你更好地掌控系统资源、优化程序运行。Shell 脚本为我们提供了丰富的命令来查看、控制和管理系统中的进程。下面我们将详细介绍如何通过命令来查看、管理进程,并控制进程的生命周期。

2.1 查看进程

查看进程是监控系统运行状态的第一步。我们可以使用不同的命令来查看系统中正在运行的进程,并获取有关每个进程的信息。

  1. ps:显示当前用户或系统中运行的进程。

    ps 是最常用的命令之一,用于查看当前的进程。可以使用 -e 参数查看所有进程,-f 参数显示进程的详细信息。

    ps        # 显示当前用户的进程
    ps -e     # 显示系统中所有的进程
    ps -ef    # 显示所有进程及详细信息
    ps aux    # 显示所有用户的进程,类似于 ps -ef
    

    输出信息通常包含以下字段:

    • PID:进程的ID
    • USER:启动进程的用户
    • CPU:CPU 使用率
    • MEM:内存使用率
    • VSZ:进程虚拟内存大小
    • RSS:进程常驻内存使用量
    • STAT:进程状态
  2. top:动态查看系统进程。

    top 命令提供一个实时更新的进程视图,显示 CPU、内存、进程等信息。它是系统管理员和开发人员经常用来监控系统性能的工具。

    top  # 显示系统的进程活动情况,按 CPU 和内存占用排序
    
    • P 键按 CPU 占用排序,按 M 键按内存占用排序。
    • q 键退出 top
  3. htop:一个更友好的 top 命令替代品。

    htop 提供了一个更友好的图形化界面,显示进程的信息并允许用户直接交互。它支持使用箭头键选择进程,进行排序和管理。

    htop  # 显示进程列表(需要安装 htop)
    

    F6 键来选择排序方式,按 F9 键来结束进程。


2.2 后台和前台进程

在 Linux 系统中,进程可以在后台或前台运行。前台进程通常占用终端,用户必须等待它完成才能继续其他操作。后台进程则允许用户在启动进程后继续在终端执行其他命令。

  1. 前台进程

    默认情况下,运行的进程会在前台执行。当在命令行输入一个命令时,它通常会在终端中显示执行过程,并且在命令完成之前不能执行其他操作。

    sleep 10  # 该命令将在前台执行,执行 10 秒钟
    
  2. 后台进程

    将进程放到后台运行可以让你在执行命令后继续使用终端。只需在命令末尾加上 & 符号即可。

    sleep 10 &  # 将命令放到后台执行
    

    当进程在后台运行时,系统会显示该进程的 PID,你可以用来后续管理进程。例如,[1] 2345 表示后台进程的编号和 PID。

  3. 查看后台进程

    可以使用 jobs 命令查看当前终端的后台进程列表:

    jobs  # 列出当前终端的所有后台任务
    
  4. 将后台进程调到前台

    若需要将后台运行的进程带回前台,可以使用 fg 命令:

    fg %1  # 将编号为 1 的后台进程调回前台
    

    这里,%1 是进程的作业编号。

  5. 将前台进程放到后台

    如果你希望将一个正在运行的前台进程放到后台,可以通过按 Ctrl + Z 键暂停该进程,然后使用 bg 命令让它继续在后台运行:

    Ctrl + Z  # 暂停前台进程
    bg       # 将暂停的进程移至后台继续执行
    

2.3 进程控制

有时需要终止或管理某个进程。Linux 提供了多种命令来控制进程的结束或发送信号。

  1. kill:发送信号终止进程。

    kill 命令用于终止一个或多个进程。它通过向进程发送信号来控制进程的行为,默认发送 SIGTERM 信号,要求进程正常退出。

    kill PID  # 终止指定 PID 的进程
    
    • 你可以通过 pstop 命令获取进程的 PID。

    • kill 还可以通过 -s 选项指定不同的信号。例如,使用 -9 强制杀死进程(SIGKILL):

      kill -9 PID  # 强制终止进程
      
  2. killall:根据进程名称终止进程。

    killall 命令允许你根据进程名称结束所有匹配的进程。这对于同时终止多个同名进程非常方便。

    killall process_name  # 终止所有名为 process_name 的进程
    
  3. pkill:根据进程名称或属性终止进程。

    pkillkillall 的一个扩展,支持更复杂的匹配条件,如根据进程的用户、终端等信息终止进程。

    pkill -u username  # 终止指定用户的所有进程
    pkill -f "pattern"  # 通过进程名称的模式匹配终止进程
    
  4. xkill:图形界面下终止进程。

    xkill 提供了一种图形化方式,允许用户点击窗口来终止对应的进程。运行 xkill 后,鼠标变为一个十字架,点击任意窗口即会结束该窗口的进程。

    xkill  # 点击图形界面上的窗口终止进程
    

3. 错误处理

在 Shell 脚本中,错误处理是非常重要的一部分,尤其是在自动化任务或系统管理脚本中。如果一个命令执行失败,我们需要及时捕获并进行相应处理。Shell 提供了多种方式来帮助我们管理错误,确保脚本的健壮性和可维护性。

3.1 退出状态码(Exit Status)

每个命令在执行后都会返回一个退出状态码,称为“退出状态”(Exit Status)或“返回码”。这个状态码用于指示命令是否成功执行。成功执行的命令通常返回 0,而失败的命令返回非零值。

  • $?:Shell 中的特殊变量 $? 用于获取上一条命令的退出状态码。我们可以通过检查这个状态码来判断命令是否成功执行,并在失败时进行错误处理。
# 执行一个命令
mkdir new_directory

# 获取上一条命令的退出状态
status=$?

# 根据退出状态判断命令是否成功执行
if [ $status -eq 0 ]; then
  echo "命令执行成功"
else
  echo "命令执行失败,退出状态码: $status"
fi

在上面的例子中,mkdir new_directory 命令会创建一个新的目录。如果目录创建成功,$? 返回 0,否则返回一个非零的状态码(例如,目录已存在时)。我们可以根据这个状态码决定接下来的操作。

常见的退出状态码示例:

  • 0:表示命令成功执行。
  • 1:表示一般错误。
  • 2:表示命令使用不正确的参数。
  • 其他:根据不同的命令,退出状态码会有所不同。

通过检查退出状态码,我们可以在脚本中做出不同的反应,确保在出现错误时能够做出相应的处理。


3.2 捕获错误和信号(trap 命令)

在脚本运行过程中,某些错误可能是不可预见的,或者脚本可能会被中断(例如,用户按下 Ctrl+C)。为了优雅地处理这些异常,我们可以使用 trap 命令来捕获错误和信号。

trap 命令 用于捕捉特定的信号并执行指定的命令。这对于清理工作(例如删除临时文件、关闭连接等)以及处理意外中断非常有用。

示例:捕获错误并执行清理操作

#!/bin/bash

# 定义一个函数,用于在脚本退出时清理资源
cleanup() {
    
    
  echo "脚本已退出,清理资源..."
  # 进行资源清理工作,如删除临时文件、关闭数据库连接等
}

# 捕获退出信号(如 Ctrl+C)和其他错误信号
trap cleanup EXIT

# 执行一些操作
echo "开始执行脚本..."
# 模拟一个错误
false  # 假设该命令执行失败

echo "脚本执行结束"

在上面的示例中,trap cleanup EXIT 命令会在脚本退出时(无论是正常退出还是由于错误退出)调用 cleanup 函数来清理资源。

常用信号:

  • EXIT:脚本退出时触发。
  • INT:捕获中断信号(通常由 Ctrl+C 引发)。
  • TERM:捕获终止信号。
  • HUP:捕获挂起信号,通常由终端断开连接时触发。

通过使用 trap,我们可以确保脚本在异常终止时也能进行必要的清理,避免留下未处理的资源或状态。


3.3 调试脚本

调试是编写 Shell 脚本时非常重要的一环,可以帮助我们发现代码中的潜在问题。Shell 提供了一些内置工具来帮助调试脚本,最常用的调试工具是 set -x

  • set -x:开启调试模式。在调试模式下,Shell 会在执行每一条命令之前输出该命令及其参数,这有助于跟踪脚本执行的每个步骤。
#!/bin/bash

# 启用调试模式
set -x

echo "This is a test script."

# 模拟一个命令
mkdir test_directory

# 关闭调试模式
set +x

在执行该脚本时,Shell 会显示每一条命令的执行过程,包括命令的输入和输出。set -x 会输出如下内容:

+ echo 'This is a test script.'
This is a test script.
+ mkdir test_directory

通过调试信息,我们可以更容易地定位到脚本中的问题,了解命令的执行顺序和变量的值。

  • set +x:用于关闭调试模式。在调试完毕后,我们可以使用 set +x 关闭调试,防止输出过多的调试信息。

示例:调试脚本

#!/bin/bash

# 启用调试模式
set -x

echo "开始调试脚本..."
ls /nonexistent_directory  # 访问不存在的文件夹,这个命令会失败

echo "脚本结束"

执行时,Shell 会显示每个步骤的详细信息,包括错误的发生位置。

调试模式非常适用于复杂脚本或出现不确定问题时,有助于快速定位错误。


3.4 调试常用技巧

除了 set -x,Shell 还提供了一些其他调试技巧:

  • set -e:启用错误检查。如果任何命令的返回状态码不为 0,脚本将立即停止执行。这个选项可以帮助我们更早地捕获并处理错误。

    set -e  # 开启错误检查
    
  • set -u:启用变量未定义错误检查。如果脚本中使用了未定义的变量,Shell 会报错并退出。

    set -u  # 开启未定义变量检查
    
  • set -v:输出命令本身,而不执行它们。这可以帮助你看到脚本中每个命令的逐行执行情况。

    set -v  # 输出命令本身