Shell脚本技巧:去除文件中字符串两端空白
在 Bash 脚本中,处理文件时经常需要清理每行两端的空白字符(包括空格、制表符、换行符等),类似于 Python 的 strip()
方法。虽然 Bash 没有直接内置函数实现这一功能,但可以通过内置功能和外部工具(如 sed
、awk
、参数扩展等)实现。本文将详细介绍几种常用方法,深入剖析其工作原理,并提供代码示例。
方法 1:使用 sed
命令
代码示例
sed 's/^[[:space:]]*\|[[:space:]]*$//g' input.txt > output.txt
原理
sed
:流编辑器(Stream Editor),逐行读取文件并应用指定的编辑命令。s/pattern/replacement/g
:替换命令,s
表示替换,pattern
是要匹配的内容,replacement
是替换结果(这里为空),g
表示全局替换。^[[:space:]]*
:^
:匹配行首。[[:space:]]
:POSIX 字符类,匹配所有空白字符(空格、制表符、换行符等)。*
:表示零个或多个匹配,匹配行首的所有连续空白字符。
\|
:正则表达式中的“或”操作符,连接两个模式。[[:space:]]*$
:$
:匹配行尾。[[:space:]]*
:匹配行尾的所有连续空白字符。
//
:将匹配的空白替换为空字符串。> output.txt
:将处理结果重定向到新文件。
执行过程
假设 input.txt
内容为:
Hello World Tab Test
sed
逐行读取。- 第一行
" Hello World "
:- 匹配开头
" "
,替换为空。 - 匹配结尾
" "
,替换为空。 - 输出:
Hello World
。
- 匹配开头
- 第二行
" Tab Test "
:- 匹配开头
" "
(制表符),替换为空。 - 匹配结尾
" "
,替换为空。 - 输出:
Tab Test
。
- 匹配开头
优点与适用场景
- 优点:支持正则表达式,处理多种空白字符,适合批量文件操作。
- 场景:清理日志文件或格式化文本数据。
方法 2:使用 awk
命令(王炸)
代码示例
awk '{$1=$1};1' input.txt > output.txt
原理
awk
:文本处理工具,按行处理文件,默认以空白字符(空格、制表符等)作为字段分隔符。{$1=$1}
:$1
:表示当前行的第一个字段。=$1
:将第一个字段重新赋值给自身。- 关键机制:
awk
在重新赋值时,会重新构建整行,自动去除行首和行尾的空白字符,同时将字段间的多个空白压缩为单个空格。
1
:- 在
awk
中,条件为真(非零)时打印整行。 - 这里
1
恒为真,等价于{print $0}
,输出处理后的行。
- 在
> output.txt
:将结果重定向到新文件。
执行过程
假设 input.txt
内容为:
Hello World Tab Test
- 第一行
" Hello World "
:$1="Hello"
,$2="World"
。- 重新构建:
Hello World
(两端空白被移除,中间多余空格压缩为一个)。 - 输出:
Hello World
。
- 第二行
" Tab Test "
:$1="Tab"
,$2="Test"
。- 重新构建:
Tab Test
。 - 输出:
Tab Test
。
优点与适用场景
- 优点:代码简洁,自动处理所有空白字符,效率高。
- 场景:快速清理简单文本文件,尤其是字段分隔明确的内容。
注意
- 如果行中只有空白字符,输出为空行。
方法 3:使用 Bash 参数扩展与 while read
代码示例
while read -r line; dotrimmed="${line#"${line%%[![:space:]]*}"}"trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"echo "$trimmed"
done < input.txt > output.txt
原理
while read -r line
:read -r
:逐行读取文件内容,-r
防止反斜杠转义。< input.txt
:将文件内容重定向到循环。
"${line#"${line%%[![:space:]]*}"}"
:${line%%[![:space:]]*}
:从$line
末尾开始,匹配最短的非空白字符之前的部分(即开头的所有空白)。${line#...}
:从$line
开头移除匹配的部分。- 结果:去掉行首空白。
"${trimmed%"${trimmed##*[![:space:]]}"}"
:${trimmed##*[![:space:]]}
:从$trimmed
开头开始,匹配最长的非空白字符之后的部分(即结尾的所有空白)。${trimmed%...}
:从$trimmed
结尾移除匹配的部分。- 结果:去掉行尾空白。
echo "$trimmed"
:输出处理后的行。> output.txt
:将结果重定向到新文件。
执行过程
假设 input.txt
内容为:
Hello World Tab Test
- 第一行
" Hello World "
:${line%%[![:space:]]*}
匹配" "
,移除后得"Hello World "
。${trimmed##*[![:space:]]}
匹配" "
,移除后得"Hello World"
。- 输出:
Hello World
。
- 第二行
" Tab Test "
:${line%%[![:space:]]*}
匹配" "
,移除后得"Tab Test "
。${trimmed##*[![:space:]]}
匹配" "
,移除后得"Tab Test"
。- 输出:
Tab Test
。
优点与适用场景
- 优点:无需外部工具,纯 Bash 实现,适合变量处理或小文件。
- 场景:需要逐行处理并结合其他逻辑时。
注意
- 对大文件性能较低,因逐行读取和处理较慢。
方法 4:使用 tr
和 sed
组合
代码示例
tr -s '[:space:]' < input.txt | sed 's/^[[:space:]]*\|[[:space:]]*$//g' > output.txt
原理
tr -s '[:space:]'
:-s
:压缩(squeeze),将连续的空白字符替换为单个空白。'[:space:]'
:匹配所有空白字符。- 作用:将行内多余空白压缩,但无法直接去除两端空白。
sed 's/^[[:space:]]*\|[[:space:]]*$//g'
:- 与方法 1 相同,去除行首和行尾的空白。
- 管道:
tr
处理后输出到sed
,再重定向到文件。
执行过程
假设 input.txt
内容为:
Hello World Tab Test
- 第一行
" Hello World "
:tr -s
:" Hello World "
。sed
:"Hello World"
。- 输出:
Hello World
。
- 第二行
" Tab Test "
:tr -s
:" Tab Test "
。sed
:"Tab Test"
。- 输出:
Tab Test
。
优点与适用场景
- 优点:结合两工具优势,处理复杂空白情况。
- 场景:需要同时压缩行内空白并去除两端空白。
总结与选择建议
方法 | 原理核心 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
sed | 正则替换 | 灵活,支持多种空白 | 需熟悉正则 | 批量清理 |
awk | 字段重新构建 | 简洁,效率高 | 行内空白压缩 | 简单文本处理 |
参数扩展+read | 字符串模式匹配 | 无外部依赖 | 性能低,语法复杂 | 小文件或变量处理 |
tr +sed | 压缩+正则替换 | 处理复杂空白 | 多步骤 | 行内+两端清理 |
- 快速批量处理:推荐
sed
或awk
。 - 小文件或变量:使用参数扩展。
- 复杂空白清理:结合
tr
和sed
。