Skip to content

Shell 脚本基础

什么是 Shell 脚本?

Shell 脚本是包含一系列命令的文本文件,由 Shell 解释执行。它可以自动化重复性任务、批量处理文件、管理系统等。

创建第一个脚本

编写脚本

bash
#!/bin/bash
# 我的第一个脚本
# 作者:Maxwell
# 日期:2025-01-09

echo "Hello, World!"
echo "当前时间: $(date)"
echo "当前用户: $USER"
echo "当前目录: $PWD"

Shebang

脚本第一行的 #! 称为 Shebang,指定解释器:

bash
#!/bin/bash          # 使用 bash
#!/bin/sh            # 使用 sh
#!/usr/bin/env bash  # 推荐,使用环境中的 bash
#!/usr/bin/env python3  # Python 脚本

运行脚本

bash
# 方法 1:添加执行权限
$ chmod +x script.sh
$ ./script.sh

# 方法 2:使用 bash 运行
$ bash script.sh

# 方法 3:使用 source(在当前 Shell 执行)
$ source script.sh
$ . script.sh

变量

定义和使用变量

bash
#!/bin/bash

# 定义变量(等号两边不能有空格)
name="Maxwell"
age=25
path="/home/maxwell"

# 使用变量
echo "Name: $name"
echo "Age: ${age}"
echo "Path: ${path}/documents"

# 只读变量
readonly PI=3.14159

# 删除变量
unset age

变量类型

bash
# 字符串
str="Hello World"

# 数字(实际上都是字符串)
num=42

# 数组
arr=(one two three)
echo ${arr[0]}    # one
echo ${arr[@]}    # 所有元素
echo ${#arr[@]}   # 数组长度

特殊变量

变量说明
$0脚本名称
$1-$9位置参数
${10}第 10 个及以后的参数
$#参数个数
$@所有参数(作为独立字符串)
$*所有参数(作为单个字符串)
$?上一个命令的退出状态
$$当前脚本的 PID
$!最后一个后台进程的 PID
bash
#!/bin/bash
echo "脚本名: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "参数个数: $#"
echo "所有参数: $@"

字符串操作

bash
str="Hello World"

# 长度
echo ${#str}         # 11

# 子串
echo ${str:0:5}      # Hello
echo ${str:6}        # World

# 替换
echo ${str/World/Bash}    # Hello Bash
echo ${str//o/O}          # HellO WOrld(全部替换)

# 删除
filename="document.txt.bak"
echo ${filename%.bak}     # document.txt(从右删除)
echo ${filename%%.*}      # document(贪婪)
echo ${filename#*.}       # txt.bak(从左删除)
echo ${filename##*.}      # bak(贪婪)

# 默认值
echo ${undefined:-default}    # 变量未定义时使用默认值
echo ${undefined:=default}    # 未定义时设置默认值
echo ${undefined:?error msg}  # 未定义时报错

引号

单引号

单引号内的内容原样输出,不解析变量:

bash
name="Maxwell"
echo 'Hello $name'    # Hello $name

双引号

双引号内解析变量和命令替换:

bash
name="Maxwell"
echo "Hello $name"    # Hello Maxwell
echo "Today is $(date +%Y-%m-%d)"

反引号和 $()

命令替换:

bash
# 反引号(旧语法)
today=`date +%Y-%m-%d`

# $()(推荐)
today=$(date +%Y-%m-%d)
files=$(ls -la)

算术运算

$(()) 语法

bash
a=10
b=3

echo $((a + b))    # 13
echo $((a - b))    # 7
echo $((a * b))    # 30
echo $((a / b))    # 3
echo $((a % b))    # 1
echo $((a ** b))   # 1000(幂运算)

# 自增自减
((a++))
((b--))
((a += 5))

let 命令

bash
let "a = 10 + 5"
let "a++"

expr 命令

bash
result=$(expr 10 + 5)
result=$(expr 10 \* 5)  # 乘法需要转义

bc 计算器(浮点数)

bash
# 整数除法
echo $((10 / 3))    # 3

# 浮点数除法
echo "scale=2; 10 / 3" | bc    # 3.33

# 复杂计算
result=$(echo "scale=4; sqrt(2)" | bc)

条件判断

if 语句

bash
#!/bin/bash

if [ 条件 ]; then
    命令
fi

if [ 条件 ]; then
    命令1
else
    命令2
fi

if [ 条件1 ]; then
    命令1
elif [ 条件2 ]; then
    命令2
else
    命令3
fi

条件表达式

文件测试

表达式说明
-e file文件存在
-f file是普通文件
-d file是目录
-r file可读
-w file可写
-x file可执行
-s file文件非空
-L file是符号链接
bash
if [ -f "/etc/passwd" ]; then
    echo "文件存在"
fi

if [ -d "/home" ]; then
    echo "目录存在"
fi

字符串测试

表达式说明
-z str字符串为空
-n str字符串非空
str1 = str2字符串相等
str1 != str2字符串不等
bash
name="Maxwell"
if [ -n "$name" ]; then
    echo "名字非空"
fi

if [ "$name" = "Maxwell" ]; then
    echo "名字匹配"
fi

数值比较

表达式说明
n1 -eq n2等于
n1 -ne n2不等于
n1 -gt n2大于
n1 -ge n2大于等于
n1 -lt n2小于
n1 -le n2小于等于
bash
age=25
if [ $age -ge 18 ]; then
    echo "成年人"
fi

逻辑运算

bash
# AND
if [ 条件1 ] && [ 条件2 ]; then
if [ 条件1 -a 条件2 ]; then

# OR
if [ 条件1 ] || [ 条件2 ]; then
if [ 条件1 -o 条件2 ]; then

# NOT
if [ ! 条件 ]; then

[[ ]] 扩展测试

[[ ]] 是 Bash 扩展,功能更强:

bash
# 支持正则匹配
if [[ "$str" =~ ^[0-9]+$ ]]; then
    echo "是数字"
fi

# 支持模式匹配
if [[ "$file" == *.txt ]]; then
    echo "是文本文件"
fi

# 更安全的字符串比较
if [[ "$name" == "Maxwell" ]]; then
    echo "匹配"
fi

case 语句

bash
#!/bin/bash

case $1 in
    start)
        echo "启动服务"
        ;;
    stop)
        echo "停止服务"
        ;;
    restart)
        echo "重启服务"
        ;;
    *)
        echo "用法: $0 {start|stop|restart}"
        exit 1
        ;;
esac

循环

for 循环

bash
# 列表循环
for item in item1 item2 item3; do
    echo $item
done

# 数组循环
arr=(one two three)
for item in ${arr[@]}; do
    echo $item
done

# 文件循环
for file in *.txt; do
    echo "处理: $file"
done

# 范围循环
for i in {1..5}; do
    echo $i
done

# C 风格循环
for ((i=0; i<5; i++)); do
    echo $i
done

# 命令输出循环
for user in $(cat /etc/passwd | cut -d: -f1); do
    echo "用户: $user"
done

while 循环

bash
#!/bin/bash

count=0
while [ $count -lt 5 ]; do
    echo "Count: $count"
    ((count++))
done

# 读取文件
while IFS= read -r line; do
    echo "行: $line"
done < file.txt

# 无限循环
while true; do
    echo "运行中..."
    sleep 1
done

until 循环

bash
count=0
until [ $count -ge 5 ]; do
    echo "Count: $count"
    ((count++))
done

循环控制

bash
# break - 退出循环
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break
    fi
    echo $i
done

# continue - 跳过本次
for i in {1..5}; do
    if [ $i -eq 3 ]; then
        continue
    fi
    echo $i
done

函数

定义和调用

bash
#!/bin/bash

# 定义函数
function greet() {
    echo "Hello, $1!"
}

# 或(POSIX 兼容)
greet() {
    echo "Hello, $1!"
}

# 调用函数
greet "Maxwell"
greet "World"

函数参数和返回值

bash
#!/bin/bash

add() {
    local a=$1
    local b=$2
    local sum=$((a + b))
    echo $sum    # 通过 echo 返回值
}

# 获取返回值
result=$(add 3 5)
echo "结果: $result"

# 使用 return(只能返回 0-255)
check_file() {
    if [ -f "$1" ]; then
        return 0
    else
        return 1
    fi
}

if check_file "/etc/passwd"; then
    echo "文件存在"
fi

局部变量

bash
#!/bin/bash

global_var="全局"

my_function() {
    local local_var="局部"
    global_var="在函数中修改"
    echo "函数内: $local_var, $global_var"
}

my_function
echo "函数外: $global_var"    # 在函数中修改
echo "函数外: $local_var"     # 空(局部变量不可见)

输入输出

读取用户输入

bash
#!/bin/bash

# 基本读取
echo -n "请输入名字: "
read name
echo "你好, $name"

# 带提示
read -p "请输入年龄: " age

# 隐藏输入(密码)
read -sp "请输入密码: " password
echo

# 设置超时
read -t 5 -p "5秒内输入: " answer

# 读取到数组
read -a arr -p "输入多个值: "
echo "第一个: ${arr[0]}"

输出

bash
# echo
echo "普通输出"
echo -n "不换行"
echo -e "支持\t转义\n字符"

# printf(格式化输出)
printf "Name: %s, Age: %d\n" "Maxwell" 25
printf "%-10s %5d\n" "Item" 100

实用示例

备份脚本

bash
#!/bin/bash

BACKUP_DIR="/backup"
SOURCE_DIR="/var/www"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/www_${DATE}.tar.gz"

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 创建备份
tar -czf "$BACKUP_FILE" "$SOURCE_DIR"

if [ $? -eq 0 ]; then
    echo "备份成功: $BACKUP_FILE"
else
    echo "备份失败"
    exit 1
fi

# 删除 7 天前的备份
find "$BACKUP_DIR" -name "www_*.tar.gz" -mtime +7 -delete

系统监控脚本

bash
#!/bin/bash

echo "=== 系统信息 ==="
echo "主机名: $(hostname)"
echo "系统: $(uname -s)"
echo "内核: $(uname -r)"
echo

echo "=== CPU 使用率 ==="
top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d% -f1
echo

echo "=== 内存使用 ==="
free -h | grep Mem
echo

echo "=== 磁盘使用 ==="
df -h | grep -v tmpfs

小结

本章介绍了 Shell 脚本基础:

  • 脚本结构:Shebang、注释、执行
  • 变量:定义、使用、特殊变量
  • 运算:算术运算、字符串操作
  • 条件判断:if、case、测试表达式
  • 循环:for、while、until
  • 函数:定义、参数、返回值
  • 输入输出:read、echo、printf

Shell 脚本是 Linux 自动化的基础,掌握它将大大提高你的工作效率。


上一章:SSH 远程连接

下一章:环境变量