4521 字
23 分钟

Linux Shell脚本编程

2026-02-02
浏览量 加载中...

Linux Shell脚本编程#

什么是Shell脚本?#

Shell脚本是一种包含一系列Shell命令的文本文件,它可以自动执行这些命令,实现自动化任务。Shell脚本是Linux系统中自动化管理和任务处理的重要工具。

常用的Shell#

  • Bash (Bourne Again Shell):最常用的Shell,是大多数Linux发行版的默认Shell
  • sh (Bourne Shell):最早的Shell,是其他Shell的基础
  • csh (C Shell):语法类似C语言的Shell
  • tcsh:csh的增强版
  • zsh:功能强大的Shell,兼容Bash
  • ksh (Korn Shell):兼容Bash和csh的Shell

Shell脚本的基本结构#

脚本的第一行#

Shell脚本的第一行通常是shebang(#!),指定执行脚本的Shell。

#!/bin/bash # 使用Bash执行脚本
#!/bin/sh # 使用sh执行脚本
#!/usr/bin/env bash # 更灵活的方式,使用环境中的bash

注释#

在Shell脚本中,使用#开始的行是注释,不会被执行。

#!/bin/bash
# 这是一个注释
# 下面的命令会创建一个目录
mkdir -p test

脚本的执行权限#

需要为Shell脚本添加执行权限才能执行。

Terminal window
# 添加执行权限
chmod +x script.sh
# 执行脚本
./script.sh

变量#

变量的定义和使用#

在Shell脚本中,变量的定义不需要类型声明,直接赋值即可。

#!/bin/bash
# 定义变量
name="World"
age=18
# 使用变量
echo "Hello, $name!"
echo "Your age is $age."
# 变量名的大括号包围(推荐)
echo "Hello, ${name}!"
echo "Your age is ${age}."
# 修改变量值
age=20
echo "Your new age is $age."

环境变量#

环境变量是系统预定义的变量,通常使用大写字母表示。

#!/bin/bash
# 显示环境变量
echo "Home directory: $HOME"
echo "Current user: $USER"
echo "Path: $PATH"
echo "Shell: $SHELL"
echo "Current directory: $PWD"
# 设置环境变量
export MY_VAR="Hello"
echo "My variable: $MY_VAR"

特殊变量#

Shell中有一些特殊变量,用于获取命令行参数、退出状态等。

#!/bin/bash
# 显示脚本名称
echo "Script name: $0"
# 显示命令行参数
echo "Number of arguments: $#"
echo "All arguments: $@"
echo "All arguments (as single string): $*"
echo "First argument: $1"
echo "Second argument: $2"
# 显示最后一个命令的退出状态
echo "Exit status: $?"
# 显示当前进程ID
echo "Process ID: $$"
# 显示后台最后一个进程的ID
echo "Last background process ID: $!"

变量的读取#

使用read命令从标准输入读取变量值。

#!/bin/bash
# 读取变量
read -p "Enter your name: " name
echo "Hello, $name!"
# 读取多个变量
read -p "Enter your name and age: " name age
echo "Hello, $name! Your age is $age."
# 隐藏输入(用于密码)
read -s -p "Enter your password: " password
echo "\nPassword entered."

运算符#

算术运算符#

Shell中的算术运算需要使用$((...))expr命令。

#!/bin/bash
# 使用$((...))进行算术运算
x=5
y=3
echo "x + y = $((x + y))"
echo "x - y = $((x - y))"
echo "x * y = $((x * y))"
echo "x / y = $((x / y))"
echo "x % y = $((x % y))"
echo "x ** y = $((x ** y))" # 幂运算
# 自增和自减
x=5
echo "x = $x"
echo "x++ = $((x++))"
echo "x = $x"
echo "++x = $((++x))"
echo "x = $x"
# 使用let命令
let x=10
echo "x = $x"
let x=x+5
echo "x = $x"

比较运算符#

比较运算符用于比较数值或字符串。

#!/bin/bash
# 数值比较
x=5
y=10
if [ $x -eq $y ]; then
echo "x equals y"
elif [ $x -ne $y ]; then
echo "x not equals y"
elif [ $x -lt $y ]; then
echo "x less than y"
elif [ $x -le $y ]; then
echo "x less than or equal to y"
elif [ $x -gt $y ]; then
echo "x greater than y"
elif [ $x -ge $y ]; then
echo "x greater than or equal to y"
fi
# 字符串比较
str1="hello"
str2="world"
if [ "$str1" = "$str2" ]; then
echo "str1 equals str2"
elif [ "$str1" != "$str2" ]; then
echo "str1 not equals str2"
elif [ -z "$str1" ]; then
echo "str1 is empty"
elif [ -n "$str1" ]; then
echo "str1 is not empty"
elif [ "$str1" "$str2" ]; then
echo "str1 less than str2"
elif [ "$str1" "$str2" ]; then
echo "str1 greater than str2"
fi

逻辑运算符#

逻辑运算符用于组合多个条件。

#!/bin/bash
# 逻辑与 (&&)
x=5
if [ $x -gt 0 ] && [ $x -lt 10 ]; then
echo "x is between 0 and 10"
fi
# 逻辑或 (||)
x=15
if [ $x -lt 0 ] || [ $x -gt 10 ]; then
echo "x is less than 0 or greater than 10"
fi
# 逻辑非 (!)
x=5
if ! [ $x -gt 10 ]; then
echo "x is not greater than 10"
fi
# 使用双括号的逻辑运算
x=5
if (( $x > 0 && $x < 10 )); then
echo "x is between 0 and 10"
fi

文件测试运算符#

文件测试运算符用于测试文件的属性。

#!/bin/bash
file="test.txt"
# 测试文件是否存在
if [ -e "$file" ]; then
echo "File exists"
else
echo "File does not exist"
fi
# 测试是否为普通文件
if [ -f "$file" ]; then
echo "Is a regular file"
fi
# 测试是否为目录
if [ -d "$file" ]; then
echo "Is a directory"
fi
# 测试是否有读取权限
if [ -r "$file" ]; then
echo "Has read permission"
fi
# 测试是否有写入权限
if [ -w "$file" ]; then
echo "Has write permission"
fi
# 测试是否有执行权限
if [ -x "$file" ]; then
echo "Has execute permission"
fi
# 测试文件是否为空
if [ -s "$file" ]; then
echo "File is not empty"
else
echo "File is empty"
fi

控制流#

if语句#

if语句用于条件判断。

#!/bin/bash
# 基本if语句
x=5
if [ $x -gt 0 ]; then
echo "x is positive"
fi
# if-else语句
x=-5
if [ $x -gt 0 ]; then
echo "x is positive"
else
echo "x is not positive"
fi
# if-elif-else语句
x=0
if [ $x -gt 0 ]; then
echo "x is positive"
elif [ $x -lt 0 ]; then
echo "x is negative"
else
echo "x is zero"
fi
# 使用双括号的if语句
x=5
if (( $x > 0 )); then
echo "x is positive"
fi
# 使用中括号的if语句(更灵活)
x=5
if [[ $x > 0 && $x < 10 ]]; then
echo "x is between 0 and 10"
fi

case语句#

case语句用于多分支条件判断,类似于其他语言的switch语句。

#!/bin/bash
# 基本case语句
read -p "Enter a number (1-3): " num
case $num in
1)
echo "You entered one."
;;
2)
echo "You entered two."
;;
3)
echo "You entered three."
;;
*)
echo "Invalid input."
;;
esac
# 使用模式匹配
read -p "Enter a string: " str
case $str in
[a-z]*)
echo "Starts with lowercase letter"
;;
[A-Z]*)
echo "Starts with uppercase letter"
;;
[0-9]*)
echo "Starts with number"
;;
*)
echo "Starts with other character"
;;
esac

for循环#

for循环用于遍历列表中的元素。

#!/bin/bash
# 遍历数字列表
for i in 1 2 3 4 5; do
echo "Number: $i"
done
# 遍历字符串列表
for fruit in apple banana cherry date; do
echo "Fruit: $fruit"
done
# 使用seq生成数字序列
for i in $(seq 1 5); do
echo "Number: $i"
done
# 使用{start..end}生成序列(Bash 4+)
for i in {1..5}; do
echo "Number: $i"
done
# 使用C风格的for循环
for ((i=1; i<=5; i++)); do
echo "Number: $i"
done
# 遍历目录中的文件
for file in *.txt; do
echo "File: $file"
done
# 遍历命令行参数
for arg in "$@"; do
echo "Argument: $arg"
done

while循环#

while循环用于在条件为真时重复执行命令。

#!/bin/bash
# 基本while循环
count=1
while [ $count -le 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
# 使用C风格的while循环
count=1
while (( $count <= 5 )); do
echo "Count: $count"
count=$((count + 1))
done
# 无限循环(使用Ctrl+C退出)
# while true; do
# echo "Hello"
# sleep 1
# done
# 从文件读取行
while read line; do
echo "Line: $line"
done < file.txt
# 读取标准输入
# echo "Enter lines (Ctrl+D to end):"
# while read line; do
# echo "You entered: $line"
# done

until循环#

until循环用于在条件为假时重复执行命令,与while循环相反。

#!/bin/bash
# 基本until循环
count=1
until [ $count -gt 5 ]; do
echo "Count: $count"
count=$((count + 1))
done
# 使用C风格的until循环
count=1
until (( $count > 5 )); do
echo "Count: $count"
count=$((count + 1))
done

break和continue#

  • break:跳出当前循环
  • continue:跳过当前循环的剩余部分,开始下一次循环
#!/bin/bash
# 使用break
for i in 1 2 3 4 5; do
if [ $i -eq 3 ]; then
break
fi
echo "Number: $i"
done
# 使用continue
for i in 1 2 3 4 5; do
if [ $i -eq 3 ]; then
continue
fi
echo "Number: $i"
done

函数#

函数的定义和调用#

#!/bin/bash
# 定义函数
greet() {
echo "Hello, $1!"
}
# 调用函数
greet "World"
greet "Alice"
# 带返回值的函数
add() {
local sum=$(( $1 + $2 ))
echo $sum
}
# 使用函数返回值
result=$(add 5 3)
echo "Sum: $result"
# 函数中的局部变量
count=10
increment() {
local count=0 # 局部变量,不会影响外部变量
count=$((count + 1))
echo "Local count: $count"
}
increment
echo "Global count: $count"
# 函数库
# 可以将常用函数放在一个文件中,然后在其他脚本中source
# source functions.sh

函数的参数#

函数可以接收参数,使用$1$2等获取。

#!/bin/bash
# 函数的参数
print_args() {
echo "Number of arguments: $#"
echo "First argument: $1"
echo "Second argument: $2"
echo "All arguments: $@"
}
# 调用函数
print_args "Hello" "World"
# 函数中的特殊变量
print_special() {
echo "Function name: $0"
echo "Last command exit status: $?"
echo "Process ID: $$"
}
print_special

输入和输出#

标准输入、输出和错误#

  • 标准输入 (stdin):文件描述符为0,默认是键盘
  • 标准输出 (stdout):文件描述符为1,默认是屏幕
  • 标准错误 (stderr):文件描述符为2,默认是屏幕

重定向#

#!/bin/bash
# 重定向标准输出到文件
echo "Hello, World!" > output.txt
# 重定向标准输出并追加到文件
echo "Hello again!" >> output.txt
# 重定向标准错误到文件
error_command 2> error.txt
# 重定向标准输出和错误到文件
command &> output.txt
# 重定向标准输出到标准错误
echo "Error message" >&2
# 从文件读取输入
while read line; do
echo "Line: $line"
done < input.txt
# 管道:将一个命令的输出作为另一个命令的输入
echo "Hello, World!" | grep "World"
# tee命令:同时输出到标准输出和文件
echo "Hello, World!" | tee output.txt

数组#

数组的定义和使用#

#!/bin/bash
# 定义数组
fruits=('apple' 'banana' 'cherry' 'date')
# 访问数组元素
echo "First fruit: ${fruits[0]}"
echo "Second fruit: ${fruits[1]}"
echo "Third fruit: ${fruits[2]}"
echo "Fourth fruit: ${fruits[3]}"
# 访问所有元素
echo "All fruits: ${fruits[@]}"
echo "All fruits: ${fruits[*]}"
# 数组长度
echo "Number of fruits: ${#fruits[@]}"
# 修改数组元素
fruits[2]='grape'
echo "Third fruit: ${fruits[2]}"
# 添加数组元素
fruits+=('elderberry')
echo "Fifth fruit: ${fruits[4]}"
echo "All fruits: ${fruits[@]}"
# 删除数组元素
unset fruits[1]
echo "All fruits after deletion: ${fruits[@]}"
# 遍历数组
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# 遍历数组(带索引)
for i in "${!fruits[@]}"; do
echo "Fruit $i: ${fruits[$i]}"
done

关联数组#

关联数组(也称为哈希表)允许使用字符串作为索引,Bash 4.0+支持。

#!/bin/bash
# 声明关联数组
declare -A colors
# 添加键值对
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"
# 访问关联数组元素
echo "Red: ${colors[red]}"
echo "Green: ${colors[green]}"
echo "Blue: ${colors[blue]}"
# 遍历关联数组的键
for color in "${!colors[@]}"; do
echo "Color: $color"
done
# 遍历关联数组的键值对
for color in "${!colors[@]}"; do
echo "$color: ${colors[$color]}"
done
# 关联数组的大小
echo "Number of colors: ${#colors[@]}"
# 删除关联数组元素
unset colors[red]
echo "Colors after deletion: ${!colors[@]}"
# 清空关联数组
unset colors
echo "Number of colors after unset: ${#colors[@]}"

算术运算#

基本算术运算#

#!/bin/bash
# 使用$((...))
x=5
y=3
# 加法
sum=$((x + y))
echo "Sum: $sum"
# 减法
diff=$((x - y))
echo "Difference: $diff"
# 乘法
product=$((x * y))
echo "Product: $product"
# 除法
quotient=$((x / y))
echo "Quotient: $quotient"
# 取模
remainder=$((x % y))
echo "Remainder: $remainder"
# 幂运算
power=$((x ** y))
echo "Power: $power"
# 自增和自减
x=5
echo "Original x: $x"
x=$((x + 1))
echo "x after increment: $x"
x=$((x - 1))
echo "x after decrement: $x"
# 使用let命令
x=5
let x=x+1
echo "x after let increment: $x"
# 使用expr命令(较旧的方式)
x=5
y=3
sum=$(expr $x + $y)
echo "Sum using expr: $sum"

浮点运算#

Shell本身不支持浮点运算,需要使用外部工具如bc

#!/bin/bash
# 使用bc进行浮点运算
x=5
y=3
# 除法
division=$(echo "scale=2; $x / $y" | bc)
echo "Division: $division"
# 其他运算
square_root=$(echo "scale=2; sqrt($x)" | bc)
echo "Square root of $x: $square_root"
exponential=$(echo "scale=2; e($x)" | bc -l)
echo "Exponential of $x: $exponential"
sine=$(echo "scale=2; s($x)" | bc -l)
echo "Sine of $x: $sine"

正则表达式#

grep命令的正则表达式#

#!/bin/bash
# 基本正则表达式
# 匹配包含"test"的行
echo "Testing 123" | grep "test"
# 匹配以"test"开头的行
echo "testing 123" | grep "^test"
# 匹配以"123"结尾的行
echo "testing 123" | grep "123$"
# 匹配包含数字的行
echo "testing 123" | grep "[0-9]"
# 匹配包含字母的行
echo "testing 123" | grep "[a-zA-Z]"
# 匹配包含"test"或"exam"的行
echo "testing" | grep -E "test|exam"
# 匹配包含3个数字的行
echo "testing 123" | grep "[0-9]{3}"
# 匹配单词边界
echo "testing" | grep "\btest\b"

sed命令的正则表达式#

#!/bin/bash
# 替换文本
echo "Hello, World!" | sed 's/World/Shell/'
# 替换所有匹配
echo "Hello, World! World!" | sed 's/World/Shell/g'
# 使用正则表达式替换
echo "Testing 123" | sed 's/[0-9]/X/g'
# 替换行首
echo "Testing 123" | sed 's/^/Prefix: /'
# 替换行尾
echo "Testing 123" | sed 's/$/ :Suffix/'

awk命令的正则表达式#

#!/bin/bash
# 匹配包含"test"的行
echo "Testing 123" | awk '/test/ {print $0}'
# 匹配以"test"开头的行
echo "testing 123" | awk '/^test/ {print $0}'
# 匹配以"123"结尾的行
echo "testing 123" | awk '/123$/ {print $0}'
# 匹配包含数字的行
echo "testing 123" | awk '/[0-9]/ {print $0}'

脚本的调试#

调试选项#

Bash提供了几个调试选项,可以帮助找出脚本中的问题。

#!/bin/bash
# 在脚本开头添加调试选项
# -x:显示执行的命令和参数
# #!/bin/bash -x
# -v:显示读取的脚本行
# #!/bin/bash -v
# -n:检查语法错误,不执行脚本
# #!/bin/bash -n
# 在脚本中启用/禁用调试
set -x # 启用调试
echo "This line will be debugged"
set +x # 禁用调试
echo "This line will not be debugged"
# 其他有用的调试选项
set -e # 遇到错误时退出
set -u # 遇到未定义变量时退出
set -o pipefail # 管道中任何命令失败时退出

常见错误#

#!/bin/bash
# 常见错误示例
# 1. 变量名和等号之间不能有空格
# name = "World" # 错误
name="World" # 正确
# 2. 变量使用时需要$符号
# echo "Hello, name!" # 错误
echo "Hello, $name!" # 正确
# 3. 命令替换使用反引号或$()
# current_dir=ls -la # 错误
current_dir=$(ls -la) # 正确
# 4. 条件判断中的空格
# if [$name == "World"]; then # 错误
if [ "$name" == "World" ]; then # 正确
echo "Hello, World!"
fi
# 5. 字符串比较使用=或==,数值比较使用-eq等
# if [ $name == "World" ]; then # 正确(字符串比较)
# if [ $age == 18 ]; then # 错误(数值比较)
# if [ $age -eq 18 ]; then # 正确(数值比较)
# 6. 数组元素的访问
# fruits=('apple' 'banana')
# echo "First fruit: $fruits[0]" # 错误
# echo "First fruit: ${fruits[0]}" # 正确
# 7. 函数调用不需要括号
# greet() {
# echo "Hello!"
# }
# greet() # 错误
# greet # 正确

实用脚本示例#

备份脚本#

#!/bin/bash
# 备份脚本
# 备份目录
BACKUP_DIR="/backup"
# 源目录
SOURCE_DIRS=("/home" "/etc" "/var/www")
# 日期格式
DATE=$(date +"%Y-%m-%d_%H-%M-%S")
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 备份每个目录
for dir in "${SOURCE_DIRS[@]}"; do
# 目录名
dir_name=$(basename "$dir")
# 备份文件名
backup_file="$BACKUP_DIR/${dir_name}_${DATE}.tar.gz"
# 执行备份
echo "Backing up $dir to $backup_file..."
tar -czf "$backup_file" "$dir"
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "Backup of $dir completed successfully!"
else
echo "Backup of $dir failed!"
fi
done
# 清理旧备份(保留最近7天的备份)
echo "Cleaning up old backups..."
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
echo "Old backups cleaned up!"
echo "Backup script completed!"

监控脚本#

#!/bin/bash
# 监控脚本
# 监控间隔(秒)
INTERVAL=5
# 监控次数
COUNT=10
# 日志文件
LOG_FILE="system_monitor.log"
# 清空日志文件
> "$LOG_FILE"
# 监控系统状态
for ((i=1; i<=COUNT; i++)); do
# 时间戳
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
# 系统负载
load=$(uptime | awk '{print $10, $11, $12}')
# 内存使用
memory=$(free -h | grep "Mem:" | awk '{print $3 "/" $2}')
# 磁盘使用
disk=$(df -h / | grep "/" | awk '{print $3 "/" $2}')
# CPU使用(简化版)
cpu=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')
# 网络流量
network=$(ifconfig eth0 | grep "RX bytes" | awk '{print $2}' | sed 's/bytes://')
# 写入日志
echo "[$timestamp] Load: $load | Memory: $memory | Disk: $disk | CPU: ${cpu}% | Network: $network bytes" >> "$LOG_FILE"
# 显示到控制台
echo "[$timestamp] Load: $load | Memory: $memory | Disk: $disk | CPU: ${cpu}% | Network: $network bytes"
# 等待
sleep $INTERVAL
done
echo "Monitoring completed! Log saved to $LOG_FILE"

系统信息脚本#

#!/bin/bash
# 系统信息脚本
echo "===================================="
echo "System Information Script"
echo "===================================="
# 主机名
echo "Hostname: $(hostname)"
# 操作系统
echo "OS: $(lsb_release -ds)"
# 内核版本
echo "Kernel: $(uname -r)"
# 架构
echo "Architecture: $(uname -m)"
# 启动时间
echo "Uptime: $(uptime -p)"
# 处理器信息
echo "CPU: $(lscpu | grep "Model name" | awk -F":" '{print $2}' | sed 's/^[ \t]*//')"
echo "CPU Cores: $(nproc)"
# 内存信息
echo "Memory: $(free -h | grep "Mem:" | awk '{print $3 "/" $2}')"
# 磁盘信息
echo "Disk Usage:"
df -h | grep -E "^/dev/"
# 网络信息
echo "Network Interfaces:"
ip addr | grep -E "^[0-9]+: " | awk -F":" '{print $2}' | sed 's/^[ \t]*//'
# 登录用户
echo "Logged in Users:"
who
# 运行进程数
echo "Running Processes: $(ps aux | wc -l)"
# 系统负载
echo "System Load: $(uptime | awk '{print $10, $11, $12}')"
echo "===================================="
echo "Script completed!"
echo "===================================="

总结#

本文介绍了Linux Shell脚本编程的基本概念和技术,包括变量、运算符、控制流、函数、输入输出、数组、算术运算、正则表达式和脚本调试等内容。通过学习这些知识,你可以编写各种自动化脚本,提高工作效率。

Shell脚本是Linux系统管理和自动化任务的强大工具,掌握Shell脚本编程是Linux系统管理员的必备技能。希望本文能够帮助你入门Shell脚本编程,并在实践中不断提高。

练习#

  1. 编写一个脚本,输出当前目录下所有文件的名称和大小。

  2. 编写一个脚本,计算1到100的和。

  3. 编写一个脚本,检查指定文件是否存在,如果存在则显示其权限和大小。

  4. 编写一个脚本,接收用户输入的两个数,计算它们的和、差、积、商。

  5. 编写一个脚本,遍历当前目录下的所有.txt文件,将其中包含”error”的行输出到error.log文件。

  6. 编写一个脚本,创建一个目录结构:test/{dir1,dir2,dir3}。

  7. 编写一个脚本,显示系统的CPU使用率、内存使用率和磁盘使用率。

  8. 编写一个脚本,备份指定目录到/home/backup目录,备份文件名为目录名加上当前日期。

  9. 编写一个脚本,检查系统中是否安装了指定的软件包,如果没有安装则提示用户。

  10. 编写一个脚本,使用函数计算两个数的最大公约数。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Linux Shell脚本编程
https://blog.vanilla.net.cn/posts/2026-02-05-linux-shell-scripting/
作者
鹁鸪
发布于
2026-02-02
许可协议
CC BY-NC-SA 4.0

评论区

目录