插件窝 干货文章 高级 Linux Shell 脚本编程系列(一)

高级 Linux Shell 脚本编程系列(一)

echo bash 变量 使用 651    来源:    2025-04-15

高级 Linux Shell 脚本编程系列(一)

1. Shell 脚本基础回顾

1.1 脚本基本结构

#!/bin/bash
# 这是一个注释
echo "Hello, World!"
  • #!/bin/bash 称为 shebang,指定解释器
  • 注释以 # 开头
  • 脚本需要有执行权限 (chmod +x script.sh)

1.2 变量使用

name="Linux"
echo "Hello, $name"  # 变量引用
readonly PI=3.14     # 只读变量
unset name           # 删除变量

1.3 特殊变量

变量 含义
$0 脚本名称
$1-$9 脚本参数
$# 参数个数
$* 所有参数
$@ 所有参数(可迭代)
$? 上条命令退出状态
$$ 当前进程PID

2. 高级变量操作

2.1 字符串操作

str="Hello World"

# 长度
echo ${#str}       # 11

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

# 替换
echo ${str/World/Linux}  # Hello Linux

# 大小写转换
echo ${str^^}      # HELLO WORLD
echo ${str,,}      # hello world

2.2 数组操作

# 定义数组
arr=("a" "b" "c")

# 访问元素
echo ${arr[1]}     # b

# 所有元素
echo ${arr[@]}

# 数组长度
echo ${#arr[@]}

# 关联数组(需要bash 4+)
declare -A map
map["key1"]="value1"
map["key2"]="value2"
echo ${map["key1"]}

2.3 变量默认值

# 如果var未设置,则使用default
${var:-default}

# 如果var未设置或为空,则使用default
${var-default}

# 如果var未设置,则设置var为default并返回
${var:=default}

# 检查变量是否设置,未设置则报错
${var:?error message}

3. 高级条件判断

3.1 文件测试操作符

if [[ -e "file.txt" ]]; then
    echo "文件存在"
fi

常用文件测试操作符:

操作符 含义
-e 文件存在
-f 是普通文件
-d 是目录
-s 文件大小不为0
-r 可读
-w 可写
-x 可执行
-L 是符号链接
-nt 比另一个文件新
-ot 比另一个文件旧

3.2 字符串比较

if [[ "$str1" == "$str2" ]]; then
    echo "字符串相等"
fi

if [[ "$str1" > "$str2" ]]; then
    echo "str1在字典序中大于str2"
fi

if [[ -z "$str" ]]; then
    echo "字符串为空"
fi

if [[ -n "$str" ]]; then
    echo "字符串非空"
fi

3.3 数值比较

if (( a > b )); then
    echo "a大于b"
fi

数值比较操作符:-eq, -ne, -gt, -ge, -lt, -le

3.4 逻辑操作符

if [[ condition1 && condition2 ]]; then
    # AND 操作
fi

if [[ condition1 || condition2 ]]; then
    # OR 操作
fi

if [[ ! condition ]]; then
    # NOT 操作
fi

4. 高级循环结构

4.1 for 循环

# 传统形式
for i in {1..5}; do
    echo $i
done

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

# 遍历数组
arr=("a" "b" "c")
for item in "${arr[@]}"; do
    echo $item
done

# 遍历文件
for file in *.txt; do
    echo "Processing $file"
done

4.2 while 和 until 循环

# while循环
count=0
while [[ $count -lt 5 ]]; do
    echo $count
    ((count++))
done

# until循环(条件为假时执行)
count=0
until [[ $count -ge 5 ]]; do
    echo $count
    ((count++))
done

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

4.3 循环控制

# 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

5. 函数高级用法

5.1 定义和调用函数

function greet() {
    local name=$1  # 局部变量
    echo "Hello, $name"
    return 0
}

greet "Alice"
echo "返回值: $?"

5.2 函数参数

function sum() {
    local total=0
    for num in "$@"; do
        ((total+=num))
    done
    echo $total
}

result=$(sum 1 2 3 4 5)
echo "总和: $result"

5.3 函数返回值

  • 使用 return 返回状态码 (0-255)
  • 使用 echoprintf 输出结果,通过命令替换捕获
function get_date() {
    date +%F
}

today=$(get_date)
echo "今天是: $today"

6. 调试技巧

6.1 调试选项

#!/bin/bash
# -x 打印执行的命令
# -e 命令失败时立即退出
# -u 使用未定义变量时报错
set -euxo pipefail

6.2 调试函数

function debug() {
    echo "DEBUG: $*" >&2
}

debug "这是一个调试信息"

6.3 使用 trap 捕获信号

function cleanup() {
    echo "清理临时文件..."
    rm -f /tmp/tempfile*
}

trap cleanup EXIT INT TERM

7. 实战示例

7.1 日志记录函数

function log() {
    local level=$1
    local message=$2
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
}

log "INFO" "脚本开始执行"

7.2 配置文件解析

# config.cfg
# key=value
# name=Alice

declare -A config
while IFS='=' read -r key value; do
    config["$key"]="$value"
done < "config.cfg"

echo "Name: ${config[name]}"

7.3 进度条显示

function progress_bar() {
    local width=50
    local percent=$1
    local filled=$((width * percent / 100))
    local empty=$((width - filled))

    printf "["
    printf "%${filled}s" | tr ' ' '#'
    printf "%${empty}s" | tr ' ' ' '
    printf "] %d%%\r" "$percent"
}

for i in {0..100..5}; do
    progress_bar $i
    sleep 0.1
done
echo

8. 最佳实践

  1. 始终在脚本开头使用 set -euo pipefail
  2. 为变量和函数使用有意义的名称
  3. 对用户输入进行验证
  4. 使用 [[ ]] 代替 [ ] 进行条件测试
  5. 使用 $(...) 代替反引号进行命令替换
  6. 为脚本和函数添加注释
  7. 处理错误和异常情况
  8. 使用小函数并保持单一职责原则
  9. 避免使用全局变量
  10. 为生产环境脚本添加日志功能

在下一篇文章中,我们将深入探讨进程控制、信号处理和并行执行等更高级的主题。