Bash 基础强化

深入掌握 Bash Shell 的核心特性,为编写高质量运维脚本打下坚实基础。

变量和数据类型

变量定义和使用

#!/bin/bash

# 变量赋值(等号两边不能有空格)
name="Linux"
version=5.15
count=100

# 变量引用
echo "System: $name"
echo "Version: ${version}"

# 只读变量
readonly PI=3.14159
# PI=3.14  # 报错:readonly variable

# 删除变量
unset count

特殊变量

#!/bin/bash

echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "所有参数: $@"
echo "参数个数: $#"
echo "上一个命令退出状态: $?"
echo "当前进程 PID: $$"
echo "上一个后台进程 PID: $!"

数组操作

#!/bin/bash

# 定义数组
servers=("web1" "web2" "web3" "db1")

# 访问元素
echo "第一个服务器: ${servers[0]}"
echo "所有服务器: ${servers[@]}"
echo "数组长度: ${#servers[@]}"

# 遍历数组
for server in "${servers[@]}"; do
    echo "处理服务器: $server"
done

# 关联数组(Bash 4.0+)
declare -A config
config[host]="192.168.1.10"
config[port]=22
config[user]="admin"

echo "SSH 连接: ${config[user]}@${config[host]}:${config[port]}"

字符串操作

字符串处理

#!/bin/bash

str="Hello, Linux World!"

# 字符串长度
echo "长度: ${#str}"

# 字符串截取
echo "截取: ${str:0:5}"        # Hello
echo "从第7个字符开始: ${str:7}"  # Linux World!

# 字符串替换
echo "${str/Linux/Unix}"        # 替换第一个
echo "${str//o/O}"              # 替换所有

# 删除子串
echo "${str#Hello, }"           # 从开头删除最短匹配
echo "${str%World!}"            # 从结尾删除最短匹配

# 大小写转换
upper="${str^^}"                # 转大写
lower="${str,,}"                # 转小写
echo "$upper"
echo "$lower"

字符串判断

#!/bin/bash

str="test.txt"

# 判断包含
if [[ $str == *".txt" ]]; then
    echo "是文本文件"
fi

# 正则匹配
if [[ $str =~ ^test\..*$ ]]; then
    echo "文件名以 test 开头"
fi

# 字符串比较
str1="abc"
str2="xyz"
if [[ $str1 < $str2 ]]; then
    echo "$str1 在 $str2 之前"
fi

条件判断

文件测试

#!/bin/bash

file="/etc/passwd"

# 文件存在性
[ -e "$file" ] && echo "文件存在"
[ -f "$file" ] && echo "是普通文件"
[ -d "$file" ] && echo "是目录"
[ -L "$file" ] && echo "是符号链接"

# 文件权限
[ -r "$file" ] && echo "可读"
[ -w "$file" ] && echo "可写"
[ -x "$file" ] && echo "可执行"

# 文件比较
if [ file1 -nt file2 ]; then
    echo "file1 比 file2 新"
fi

数值比较

#!/bin/bash

num1=10
num2=20

# 算术比较
if [ $num1 -eq $num2 ]; then echo "相等"; fi
if [ $num1 -ne $num2 ]; then echo "不等"; fi
if [ $num1 -lt $num2 ]; then echo "$num1 小于 $num2"; fi
if [ $num1 -gt $num2 ]; then echo "$num1 大于 $num2"; fi
if [ $num1 -le $num2 ]; then echo "$num1 小于等于 $num2"; fi
if [ $num1 -ge $num2 ]; then echo "$num1 大于等于 $num2"; fi

# 使用 (( )) 进行算术比较
if (( num1 < num2 )); then
    echo "更简洁的比较方式"
fi

逻辑运算

#!/bin/bash

age=25
score=85

# AND 运算
if [ $age -ge 18 ] && [ $score -ge 60 ]; then
    echo "成年且及格"
fi

# OR 运算
if [ $age -lt 18 ] || [ $score -lt 60 ]; then
    echo "未成年或不及格"
fi

# NOT 运算
if [ ! -f "nofile.txt" ]; then
    echo "文件不存在"
fi

# 使用 [[ ]] 的逻辑运算
if [[ $age -ge 18 && $score -ge 60 ]]; then
    echo "推荐使用 [[ ]]"
fi

循环控制

for 循环

#!/bin/bash

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

# 遍历列表
for item in apple banana orange; do
    echo "水果: $item"
done

# 遍历文件
for file in /etc/*.conf; do
    echo "配置文件: $file"
done

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

# 遍历数字序列
for num in {1..10}; do
    echo $num
done

# 带步长的序列
for num in {0..100..10}; do
    echo $num
done

while 循环

#!/bin/bash

# 基本 while 循环
count=1
while [ $count -le 5 ]; do
    echo "计数: $count"
    ((count++))
done

# 读取文件
while IFS= read -r line; do
    echo "行内容: $line"
done < /etc/hosts

# 无限循环
while true; do
    echo "按 Ctrl+C 退出"
    sleep 1
done

until 循环

#!/bin/bash

count=1
until [ $count -gt 5 ]; do
    echo "计数: $count"
    ((count++))
done

break 和 continue

#!/bin/bash

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

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

函数

函数定义和调用

#!/bin/bash

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

# 函数定义方式2
greet2() {
    echo "Hi, $1!"
}

# 调用函数
greet "Linux"
greet2 "World"

# 带返回值的函数
add() {
    local result=$(($1 + $2))
    echo $result
}

sum=$(add 10 20)
echo "和: $sum"

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

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

局部变量和全局变量

#!/bin/bash

global_var="全局变量"

test_scope() {
    local local_var="局部变量"
    echo "函数内: $global_var"
    echo "函数内: $local_var"
}

test_scope
echo "函数外: $global_var"
# echo "函数外: $local_var"  # 错误:未定义

实用技巧

命令替换

#!/bin/bash

# 反引号(旧式)
date=`date +%Y%m%d`

# $() 推荐方式
date=$(date +%Y%m%d)
hostname=$(hostname)

echo "备份文件: backup_${hostname}_${date}.tar.gz"

参数展开

#!/bin/bash

# 默认值
echo "${var:-default}"        # 如果 var 未设置,使用 default
echo "${var:=default}"        # 如果 var 未设置,设置为 default
echo "${var:+alternate}"      # 如果 var 已设置,使用 alternate
echo "${var:?error message}"  # 如果 var 未设置,显示错误并退出

Here Document

#!/bin/bash

# 多行输入
cat << EOF > config.txt
Server: production
Port: 8080
Debug: false
EOF

# 变量展开
cat << EOF
当前用户: $USER
当前目录: $PWD
EOF

# 禁止变量展开
cat << 'EOF'
原样输出: $USER
EOF

进程替换

#!/bin/bash

# 比较两个命令的输出
diff <(ls dir1) <(ls dir2)

# 将命令输出作为文件
while read line; do
    echo "行: $line"
done < <(ls -l)

调试技巧

#!/bin/bash

# 启用调试模式
set -x          # 打印执行的命令
set -e          # 遇到错误立即退出
set -u          # 使用未定义变量时报错
set -o pipefail # 管道中任一命令失败则失败

# 组合使用
set -euo pipefail

# 调试函数
debug() {
    if [ "${DEBUG:-0}" = "1" ]; then
        echo "[DEBUG] $@" >&2
    fi
}

# 使用
debug "This is a debug message"

# 运行时启用调试
# DEBUG=1 ./script.sh

下一步:学习 高级脚本技巧 章节。