Appearance
08|字符串处理
运维脚本里到处是字符串——文件路径、日志内容、服务名。从路径里取出文件名、从日志行里截取时间、把变量值替换成别的,这些操作很常见。
调用 sed、awk 能处理复杂字符串,但简单的截取替换用 Shell 自带的 ${} 语法就够,不用开外部进程,更快更直接。本篇讲这套 ${} 字符串操作,再讲路径处理。
一、取长度
${#var} 取字符串长度:
bash
name="hello world"
echo "${#name}" # 11二、截取子串
${var:起始:长度} 截取子串,下标从 0 开始:
bash
name="hello world"
echo "${name:0:5}" # hello
echo "${name:6:5}" # world
echo "${name:6}" # world(不写长度取到末尾)负数下标从末尾数,但负号前要有空格或括号防止和 ${var:-默认} 混淆:
bash
echo "${name: -5}" # world(取最后 5 个字符)三、删除匹配(掐头去尾)
这是路径处理的核心。# 从开头删最短匹配,## 删最长匹配;% 从末尾删最短,%% 删最长。
bash
path="/var/log/nginx/access.log"
# 从开头删(掐头)
echo "${path#*/}" # var/log/nginx/access.log(删到第一个 /)
echo "${path##*/}" # access.log(删到最后一个 /,得到文件名)
# 从末尾删(去尾)
echo "${path%/*}" # /var/log/nginx(删掉最后的 /文件名,得到目录)
echo "${path%%/*}" # (空,删到最后一个 /)记住两个最常用的:
bash
filename="${path##*/}" # 取文件名:access.log
dirname="${path%/*}" # 取目录:/var/log/nginx这比 basename 和 dirname 命令快——不开外部进程。但 basename/dirname 处理特殊路径更稳,复杂路径还是用命令。
四、替换
${var/旧/新} 替换第一个匹配,${var//旧/新} 替换所有:
bash
text="hello world hello"
echo "${text/hello/hi}" # hi world hello(只换第一个)
echo "${text//hello/hi}" # hi world hi(全换)替换支持通配符:
bash
file="access.log.2026-06-23"
echo "${file/log.*/log}" # access.log(从 log 后面全删)五、大小写转换
Bash 4+ 支持大小写转换:
bash
name="Hello"
echo "${name^^}" # HELLO(转大写)
echo "${name,,}" # hello(转小写)六、默认值和报错
变量可能为空时,给默认值或报错:
bash
user=""
echo "${user:-deploy}" # deploy(user 为空用默认值,不改变 user)
echo "${user:=deploy}" # deploy(user 为空用默认值,并赋给 user)
echo "${user:?必须指定用户}" # 报错退出:user: 必须指定用户:- 给默认值不改原变量,:= 给默认值并改原变量,:? 为空时报错退出。参数校验常用 :?。
七、路径处理的实际例子
备份脚本里,从一个完整路径拆出目录、文件名、扩展名:
bash
#!/usr/bin/env bash
set -euo pipefail
file="/var/log/nginx/access.log"
dir="${file%/*}" # /var/log/nginx
name="${file##*/}" # access.log
base="${name%.*}" # access(去掉扩展名)
ext="${name##*.}" # log(取扩展名)
echo "目录: $dir"
echo "文件名: $name"
echo "基本名: $base"
echo "扩展名: $ext"text
目录: /var/log/nginx
文件名: access.log
基本名: access
扩展名: log八、用参数扩展避免外部命令
很多字符串操作 Shell 自带就能做,不必调用 sed、awk、cut。对比如下:
bash
# 用 cut 取 IP
ip=$(echo "from 192.168.1.1" | cut -d' ' -f2)
# 用参数扩展(更快)
line="from 192.168.1.1"
ip="${line#from }"参数扩展在 Shell 内部完成,不启动子进程,循环里大量调用时性能差距明显。能用 ${} 就别开外部命令。
但复杂场景(正则匹配、多行处理)还是用 sed/awk/grep,${} 处理不了。下一批篇章专门讲这三个工具。
九、提取日志字段
实战中经常从一行日志里提取字段。用 ${} 配合 read:
bash
log='2026-06-23 14:30:01 ERROR [nginx] connection refused'
read -r date time level rest <<< "$log"
echo "时间: $date $time"
echo "级别: $level"
echo "剩余: $rest"read 按空格拆分赋给变量,<<< 把字符串当输入。复杂的字段提取(带正则)留给 awk。
学会了之后
${} 系列操作覆盖了大部分字符串处理:长度、截取、掐头去尾、替换、大小写、默认值。路径处理里 ##*/ 取文件名、%/* 取目录这两个最常用,背下来。
下一篇讲函数——把重复的代码块封装起来复用。