在Bash中按分隔符拆分字符串的方法

在Bash中按分隔符拆分字符串的方法

技术背景

在Bash脚本编程中,经常会遇到需要按特定分隔符拆分字符串的需求,例如处理CSV文件、解析日志等。掌握字符串拆分的方法对于数据处理和脚本自动化非常重要。

实现步骤

方法一:使用IFS和read命令

可以设置内部字段分隔符(IFS)变量,然后让它解析成一个数组。

示例代码:

1
2
3
4
5
6
IN="[email protected];[email protected]"
IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
# process "$i"
echo "$i"
done

上述代码首先将IFS设置为分号,然后使用read -ra将输入字符串按分号拆分成数组ADDR,最后通过循环遍历数组元素。

方法二:使用Parameter Expansion

通过替换字符串中的分隔符为空格,然后将其解释为数组。

示例代码:

1
2
3
IN="[email protected];[email protected]"
arrIN=(${IN//;/ })
echo ${arrIN[1]} # Output: [email protected]

这里使用了参数扩展语法,将字符串IN中的所有分号替换为空格,然后将结果存储在数组arrIN中。

方法三:使用cut命令

cut命令可以用于从字符串中提取特定字段。

示例代码:

1
2
echo "[email protected];[email protected]" | cut -d ";" -f 1
echo "[email protected];[email protected]" | cut -d ";" -f 2

该命令通过-d指定分隔符,-f指定要提取的字段。

方法四:使用tr命令

tr命令可以用于字符替换,将分隔符替换为换行符,然后逐行处理。

示例代码:

1
2
3
4
5
6
IN="[email protected];[email protected]"
for i in $(echo $IN | tr ";" "\n")
do
# process
echo "$i"
done

方法五:使用mapfile命令(Bash >= 4.4)

mapfile命令可以将输入按指定分隔符拆分成数组。

示例代码:

1
2
3
4
5
6
IN="[email protected];[email protected]"
mapfile -td \; fields <<<"$IN"
fields[-1]=${fields[-1]%$'\n'} # drop '\n' added on last field, by '<<<'
for x in "${fields[@]}"; do
echo "> [$x]"
done

方法六:适用于多种shell的通用方法

使用字符串替换语法提取子字符串。

示例代码:

1
2
3
4
5
6
7
8
9
IN="[email protected];[email protected];Full Name <[email protected]>"
while [ "$IN" != "$iter" ]; do
# extract the substring from start of string up to delimiter.
iter=${IN%%;*}
# delete this first "element" AND his separator, from $IN.
IN="${IN#$iter;}"
# Print (or doing anything with) the first "element".
printf '> [%s]\n' "$iter"
done

核心代码

以下是一些核心代码示例总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 使用IFS和read
IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
echo "$i"
done

# 使用Parameter Expansion
arrIN=(${IN//;/ })
echo ${arrIN[1]}

# 使用cut
echo "[email protected];[email protected]" | cut -d ";" -f 1

# 使用tr
for i in $(echo $IN | tr ";" "\n"); do
echo "$i"
done

# 使用mapfile (Bash >= 4.4)
mapfile -td \; fields <<<"$IN"
fields[-1]=${fields[-1]%$'\n'}
for x in "${fields[@]}"; do
echo "> [$x]"
done

# 通用方法
while [ "$IN" != "$iter" ]; do
iter=${IN%%;*}
IN="${IN#$iter;}"
printf '> [%s]\n' "$iter"
done

最佳实践

  • 当字符串中不包含空格时,可以使用Parameter Expansion方法,代码简洁。
  • 如果需要处理大文件的字段提取,优先使用cut命令。
  • 对于Bash 4.4及以上版本,mapfile命令是一个高效且功能强大的选择。
  • 当需要编写跨多种shell的脚本时,使用通用的字符串替换方法。

常见问题

1. 字符串包含空格

如果原始字符串包含空格,需要正确设置IFS。

1
IFS=':'; arrIN=($IN); unset IFS;

2. 分隔符是换行符

当分隔符是换行符时,可以这样设置IFS:

1
IFS=$'\n'; arrIN=($IN); unset IFS;

3. 性能问题

使用cut命令时,如果需要重复提取字段,会产生大量的fork操作,性能较低。可以比较不同方法的执行时间,选择合适的方法。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
splitByCut() {
local i=1
while iter=$(echo "$1"|cut -d\; -f$i) ; [ -n "$iter" ]; do
printf '> [%s]\n' "$iter"
i=$((i+1))
done
}

splitByMapFile() {
iterMF() {
local seq=$1 dest="${2%$'\n'}"
[[ $2 ]] && printf "> [%s]\n" "$dest"
}
mapfile <<<"${1//;/$'\n'}" -tc 1 -C iterMF
}

IN="[email protected];[email protected];Full Name <[email protected]>"
printf -v in40 %333s
in40=${in40// /$IN;}
in40=${in40%;}

start=${EPOCHREALTIME/.};splitByMapFile "$in40" | md5sum;elap=00000$((${EPOCHREALTIME/.}-start))
printf 'Elapsed: %.4f secs.\n' ${elap::-6}.${elap: -6}

start=${EPOCHREALTIME/.};splitByCut "$in40" | md5sum;elap=00000$((${EPOCHREALTIME/.}-start))
printf 'Elapsed: %.4f secs.\n' ${elap::-6}.${elap: -6}

通过比较可以发现,mapfile方法的性能明显优于cut方法。


在Bash中按分隔符拆分字符串的方法
https://119291.xyz/posts/2025-05-12.split-string-by-delimiter-in-bash/
作者
ww
发布于
2025年5月12日
许可协议