数组

# 定义
fruits=("apple" "banana" "cherry")

# 访问
echo ${fruits[0]}              # apple
echo ${fruits[1]}              # banana

# 长度
echo ${#fruits[@]}             # 3

# 所有元素
echo "${fruits[@]}"            # apple banana cherry
for f in "${fruits[@]}"; do
    echo "$f"
done

# 追加
fruits+=("date")
echo "${fruits[@]}"            # apple banana cherry date

# 切片
echo "${fruits[@]:1:2}"        # banana cherry(从下标 1,取 2 个)

# 修改
fruits[0]="apricot"

# 删除
unset 'fruits[1]'              # 删除 banana(注意单引号)
unset fruits                   # 删除整个数组

⚠ 引号要紧——"${fruits[@]}" 保留每个元素是独立的;不加引号会按空格切。

关联数组(Bash 4+)

declare -A user
user[name]="alice"
user[age]=30
user[email]="alice@example.com"

echo "${user[name]}"           # alice
echo "${!user[@]}"             # 所有键:name age email
echo "${user[@]}"              # 所有值

# 遍历
for key in "${!user[@]}"; do
    echo "$key = ${user[$key]}"
done

字符串长度

s="hello world"
echo ${#s}                     # 11

字符串截取(参数展开)

s="hello world"

# 取子串
echo ${s:6}                    # world(从下标 6 到末尾)
echo ${s:0:5}                  # hello(从 0 取 5 个)
echo ${s: -5}                  # world(最后 5 个,注意空格)

# 替换
echo ${s/world/bash}           # hello bash      (首次匹配)
echo ${s//l/L}                 # heLLo worLd     (全部匹配)
echo ${s/#hello/Hi}            # Hi world        (行首匹配)
echo ${s/%world/Bash}          # hello Bash      (行尾匹配)

# 删除前缀
file="/path/to/file.txt"
echo ${file#*/}                # path/to/file.txt   (最短匹配前缀)
echo ${file##*/}               # file.txt           (最长匹配,等同 basename)

# 删除后缀
echo ${file%.*}                # /path/to/file      (最短)
echo ${file%%.*}               # /path/to/file      (最长,这例子结果一样)

filename="report.tar.gz"
echo ${filename%.*}            # report.tar
echo ${filename%%.*}           # report
echo ${filename##*.}           # gz

# 大小写转换(Bash 4+)
s="Hello"
echo ${s^^}                    # HELLO
echo ${s,,}                    # hello
echo ${s^}                     # Hello(首字母大写)

实战:路径拆解

path="/var/log/nginx/access.log"

dir="${path%/*}"
file="${path##*/}"
name="${file%.*}"
ext="${file##*.}"

echo "目录: $dir"     # /var/log/nginx
echo "文件: $file"    # access.log
echo "名: $name"      # access
echo "扩展名: $ext"   # log

等同 dirname basename 但不需要外部命令。

实战:拆 CSV

line="alice,30,engineer"

# 用 IFS 切分
IFS=',' read -ra fields <<< "$line"

echo "${fields[0]}"            # alice
echo "${fields[1]}"            # 30
echo "${fields[2]}"            # engineer

或:

IFS=',' read -r name age job <<< "$line"
echo "$name $age $job"

实战:批量改文件后缀

for f in *.jpeg; do
    mv "$f" "${f%.jpeg}.jpg"
done

实战:从 URL 拿域名

url="https://api.example.com/v1/users"

# 去掉协议
no_proto="${url#*://}"          # api.example.com/v1/users

# 域名
domain="${no_proto%%/*}"        # api.example.com

# 顶级域名
tld="${domain##*.}"             # com

echo "$domain / $tld"

下一篇:调试与脚本规范。