Skip to content

08|字符串处理

运维脚本里到处是字符串——文件路径、日志内容、服务名。从路径里取出文件名、从日志行里截取时间、把变量值替换成别的,这些操作很常见。

调用 sedawk 能处理复杂字符串,但简单的截取替换用 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

这比 basenamedirname 命令快——不开外部进程。但 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 自带就能做,不必调用 sedawkcut。对比如下:

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

学会了之后

${} 系列操作覆盖了大部分字符串处理:长度、截取、掐头去尾、替换、大小写、默认值。路径处理里 ##*/ 取文件名、%/* 取目录这两个最常用,背下来。

下一篇讲函数——把重复的代码块封装起来复用。