// Linux · 2025-10-13

Shell脚本技巧:去除文件中字符串两端空白

在 Bash 脚本开发或日常文本处理中,清理每行首尾的空白字符(空格、制表符、换行符等)是高频需求,这一操作类似 Python 的 strip() 方法。Bash 本身未内置直接的 strip 函数,但可通过 awk、sed 等经典文本处理工具,或 Bash 内置的参数扩展特性实现。

一、首选方案:awk 命令

awk 是面向行和字段的文本处理工具,其内置的字段拆分与行重构机制,让它成为清理首尾空白的最优选择——代码极简,执行效率高,还能顺带规整字段间的多余空白。

1. 核心代码示例

# 基础版:自动过滤空行(空行$1为空,$1=$1为假,不打印)
awk '$1=$1' input.txt > output.txt

# 增强版:保留空行(强制打印,避免空行丢失)
awk '{$1=$1; print}' input.txt > output.txt

2. 底层原理剖析

要理解该命令的核心,需先掌握 awk 处理行的底层逻辑:

  • 字段拆分:awk 默认以「任意空白符(空格、制表符、换行符)」为分隔符,将每行拆分为 $1(第一个字段)、$2(第二个字段)……$NF(最后一个字段),且拆分时会自动忽略行首/行尾的空白。例如行 Hello World 会被拆分为 $1="Hello"、$2="World",而原始整行内容(含首尾空格)仍保留在 $0 中。
  • 行重构触发:$1=$1 看似是“将 $1 赋值给自身”的无意义操作,但 awk 规定:修改任意字段($n)会触发整行($0)的重新拼接。拼接时,awk 会用「输出字段分隔符(OFS,默认是单个空格)」连接所有字段,最终生成新的 $0。
  • 空白清理效果:重构后的 $0 不再包含行首/行尾的空白(拆分时已忽略),且字段间的多个空白会被压缩为单个空格(OFS 特性)。
  • 打印逻辑:awk 遵循“模式-动作”语法,$1=$1 作为模式,结果为真时执行默认动作 print $0;增强版 {print} 则强制打印,避免空行因 $1=$1 为假而丢失。

3. 执行过程演示

假设 input.txt 内容如下(含行首/尾空格、制表符,字段间多空格):

  Hello   World  
	Tab	Test	
(空行)

执行 awk '{$1=$1; print}' input.txt 后:

  1. 第一行 Hello World :拆分得到 $1=Hello、$2=World,重构后 $0=Hello World,输出 Hello World;
  2. 第二行 Tab Test :拆分得到 $1=Tab、$2=Test,重构后 $0=Tab Test,输出 Tab Test;
  3. 空行:$1 为空,但 {print} 强制打印,保留空行。

最终 output.txt 内容:

Hello World
Tab Test

二、灵活方案:sed 命令

sed(流编辑器)通过正则表达式匹配并替换文本,适合对空白清理规则有精准控制的场景(比如仅清理空格、保留制表符,或仅清理行首不清理行尾)。

1. 核心代码示例

# 通用版:清理行首/尾所有空白字符(空格、制表符等)
sed 's/^[[:space:]]*\|[[:space:]]*$//g' input.txt > output.txt

# 简化版(GNU sed 兼容):用 -E 启用扩展正则,语法更简洁
sed -E 's/^[[:space:]]+|[[:space:]]+$//g' input.txt > output.txt

# 定制版:仅清理行首/尾空格(不处理制表符)
sed 's/^ *\| *$//g' input.txt > output.txt

2. 底层原理剖析

sed 逐行读取文本,对每行执行指定的正则替换命令,核心参数解析:

  • s/pattern/replacement/g:全局替换命令,s 表示“替换”,g 表示“全局匹配(一行内所有符合条件的内容)”;
  • ^[[:space:]]*:匹配行首的空白字符:^ 锚定行首,[[:space:]] 是 POSIX 标准字符类(匹配所有空白,跨平台兼容),* 匹配零个或多个(+ 匹配一个或多个,更精准);
  • \|(基础版)/|(扩展版):正则“或”操作符,连接“行首空白”和“行尾空白”两个匹配模式;
  • [[:space:]]*$:匹配行尾的空白字符,$ 锚定行尾;
  • //:将匹配到的空白字符替换为空字符串,实现“删除”效果。

3. 执行过程演示

仍以包含首尾空白的 input.txt 为例:

  Hello World  
	Tab Test	

执行 sed -E 's/^[[:space:]]+|[[:space:]]+$//g' input.txt 后:

  1. 第一行 Hello World :匹配行首的 和行尾的 ,均替换为空,输出 Hello World;
  2. 第二行 Tab Test :匹配行首的制表符 和行尾的 ,均替换为空,输出 Tab Test。

三、进阶方案:Bash 内置参数扩展

若场景限制无法使用 awk/sed(如极简环境),可通过 Bash 内置的参数扩展实现单行空白清理,无需调用外部工具。

1. 核心代码示例

# 逐行读取文件,清理每行首尾空白
while IFS= read -r line; do
  # 清理行首空白:移除开头所有空白字符
  line_ltrim="${line##+([[:space:]])}"
  # 清理行尾空白:移除结尾所有空白字符
  line_strip="${line_ltrim%%+([[:space:]])}"
  # 输出处理后的行
  echo "$line_strip"
done < input.txt > output.txt

2. 底层原理剖析

Bash 参数扩展通过通配符匹配实现字符串截取,核心语法:

  • ${var##pattern}:从变量开头删除最长匹配 pattern 的内容,+([[:space:]]) 匹配一个或多个空白字符(需开启 extglob 扩展,默认已开启);
  • ${var%%pattern}:从变量结尾删除最长匹配 pattern 的内容;
  • IFS= read -r line:IFS= 避免 read 命令自动忽略行首空白,-r 保留反斜杠等特殊字符,保证读取原始行内容。

3. 注意事项

该方案纯 Bash 实现,但需逐行循环处理,大文件场景下效率低于 awk/sed,适合小文件或嵌入式 Bash 环境。

四、方案对比与选型建议

方案 优点 缺点 适用场景
awk 代码极简、执行高效、自动规整字段间空白 无法单独保留行首/尾某一侧空白 通用场景(推荐首选)
sed 正则灵活、可精准定制清理规则 语法稍复杂,大文件效率略低于 awk 需精准控制清理范围(如仅清空格)
Bash 内置 无外部工具依赖、环境兼容性极强 逐行循环效率低,代码量大 极简环境、小文件处理

五、总结

清理行首尾空白是 Bash 文本处理的基础操作,其中 awk '$1=$1' 是兼顾简洁与效率的最优解,适合绝大多数场景;sed 适合需要精准控制正则匹配的定制化场景;Bash 内置参数扩展则是无外部工具依赖时的兜底方案。掌握这三种方法,可覆盖从日常脚本到特殊环境的所有空白清理需求。

原文 https://blog.csdn.net/2301_79518550/article/details/146964991