Shell 分割字符串的常用方法

处理日志、配置文件或命令输出时,按分隔符把一个字符串切成几段是最常见的操作之一。Shell 下能做这件事的工具不少:cutawkIFS、参数展开、tr 各有适用场景,本文逐一给出示例。

以下示例统一使用这个字符串:

str="192.168.1.10,root,22"

cut:按单字符分隔符取列

cut 最简单,-d 指定分隔符(只能是单个字符),-f 指定取第几列:

echo "$str" | cut -d',' -f1    # 输出 192.168.1.10
echo "$str" | cut -d',' -f2    # 输出 root
echo "$str" | cut -d',' -f1,3  # 输出 192.168.1.10,22
echo "$str" | cut -d',' -f2-   # 输出 root,22(第 2 列到结尾)

awk -F:更灵活的取列

awk-F 指定分隔符,支持多字符和正则,还能在取列的同时做进一步处理:

echo "$str" | awk -F',' '{print $1}'     # 输出 192.168.1.10
echo "$str" | awk -F',' '{print $2}'     # 输出 root
echo "$str" | awk -F',' '{print NF}'     # 输出 3,NF 是字段个数
echo "$str" | awk -F',' '{print $1 " " $2}'  # 输出 192.168.1.10 root

-F 支持正则,比如按一个或多个空白字符分割:

echo "a   b c" | awk -F' +' '{print $2}'    # 输出 b

IFS:让 Shell 原生拆分

IFS(Internal Field Separator)是 Shell 的内部字段分隔符变量。临时把它改成目标分隔符,再用 read 拆到多个变量里,是纯 Shell 的经典写法:

IFS=',' read -r ip user port <<< "$str"
echo "$ip"     # 输出 192.168.1.10
echo "$user"   # 输出 root
echo "$port"   # 输出 22
  • IFS=',' read ...:把 IFS 的修改限定在这条命令内,不会污染当前 Shell;
  • <<< 是 Here String,把字符串作为 read 的标准输入;
  • -r 防止反斜杠被转义。

分割目录路径(按 : 分隔)也常用这种思路:

IFS=':' read -r dir1 dir2 dir3 <<< "$PATH"

参数展开:不启动子进程的最快方式

利用 ${var#pattern}${var%pattern} 等参数展开,可以不产生任何子进程就完成切分,在循环里处理大量数据时性能最好:

写法 含义
${var#*/} 从头删除最短匹配 */ 的部分(删掉第一个分隔符及之前的内容)
${var##*/} 从头删除最长匹配 */ 的部分(删掉最后一个分隔符及之前的内容)
${var%/*} 从尾删除最短匹配 /* 的部分(删掉最后一个分隔符及之后的内容)
${var%%/*} 从尾删除最长匹配 /* 的部分(删掉第一个分隔符及之后的内容)

示例:

path="/usr/local/bin/ls"
echo ${path#*/}     # 输出 usr/local/bin/ls
echo ${path##*/}    # 输出 ls(等价于 basename)
echo ${path%/*}     # 输出 /usr/local/bin(等价于 dirname)
echo ${path%%/*}    # 输出空字符串

# 取第一个逗号前的内容
echo ${str%%,*}     # 输出 192.168.1.10
# 取第一个逗号后的内容
echo ${str#*,}      # 输出 root,22
# 取最后一个逗号后的内容
echo ${str##*,}     # 输出 22

配合循环可以逐段弹出:

list="a,b,c"
while [ -n "$list" ]; do
    echo "${list%%,*}"
    list=${list#*,}
done
# 依次输出 a b c(最后一段没有逗号时,list 整体作为末项输出)

tr:把分隔符”压平”后交给其他命令

tr 本身不做字段切分,它擅长字符替换。把分隔符统一替换成空格或换行后,往往就能直接配合其他工具使用了:

# 逗号转空格,配合 for 循环遍历
for item in $(echo "$str" | tr ',' ' '); do
    echo "$item"
done
# 依次输出三行:192.168.1.10 / root / 22

# 逗号转换行,每行一个字段
echo "$str" | tr ',' '\n'

小结

  • 临时取一两列:cut 最省事;
  • 需要正则分隔符或对字段加工:用 awk -F
  • 纯 Shell、要拆进多个变量:用 IFS + read
  • 性能敏感、只需取头尾字段:用参数展开 ${var%%/*}${var#*/} 等;
  • 分隔符不统一、想先归一化:用 tr 转换后再处理。

根据”分隔符是否固定、字段取在哪头、是否在意性能”来选,基本就能覆盖日常所有场景。


   转载规则


《Shell 分割字符串的常用方法》 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
ln -s 软链接使用指南与常见误区 ln -s 软链接使用指南与常见误区
在 Linux 下,ln -s 用来创建软链接(也叫符号链接),作用类似 Windows 里的快捷方式:它指向真实的源文件或源目录,访问链接就等于访问源。本文整理了软链接的常见用法、容易踩的坑,以及它与硬链接的区别。 基本用法创建软链接的命
2020-11-29
下一篇 
获取 Shell 脚本所在目录的绝对路径 获取 Shell 脚本所在目录的绝对路径
写脚本时经常需要引用脚本同目录下的其他文件(配置、日志目录、依赖脚本等)。这时很多人的第一反应是用 pwd 或者 $0,但这两种做法都有陷阱。总结一下,最可靠的做法其实就是一条命令: root_path=$(cd $(dirname ${B
2020-11-15
  目录