通常来说,使用 shell 脚本时候不需要太顾及程序的效率性,能完成任务,只要不是太慢,都可以接受。
但在一些特定应用场合,脚本效率也是一个硬性限制,不能耗时太久,比如说,用 shell 来编写一些处理 HTTP 请求的 CGI 程序,脚本的执行效率直接影响了 HTTP 的响应速度。
在 shell 中分割字符串有多种方法,比如利用 awk、cut 工具,还有内置方法,虽然效果相同,但是执行效率差距较大,尤其在处理大数据量上更是显著。
用 cut 工具分割字符串一般是以下用法:
1
2
|
# -f 指定列序号 -d 指定分隔符
echo $line | cut -f1 -d ' '
|
用 awk 工具分割字符串类似:
1
2
3
|
# $1 代表第一列 以此类推
# -F 指定分割符号
echo $line | awk -F ' ' {'print $1'}
|
内部方法分割字符串,用的是 IFS
变量和 set
命令:
1
2
3
4
5
6
|
# 设置分隔符
IFS=" "
# 分割 line
set -- $line
# $1 代表第一列,以此类推
echo $1
|
在 shell 脚本中,使用 awk/cut 这些工具在运行时会创建 sub-shell 来运行,相当于 C 语言中的 system
/popen
调用,当需要使用的次数比较多时,这种调用会显著降低脚本的执行速度(创建进程是比较昂贵的)。
而使用内置方法则无需调用外部工具,执行起来效率更高,没有额外开销。
以处理 1000 行的空格分割的数字并分别累加为例,其文件内容大概如下:
1
2
3
4
5
6
7
8
|
1 25 30 100
2 26 33 102
3 27 36 104
...
999 1023 3024 2096
1000 1024 3027 2098
|
保存为文件 test.data
。然后编写2个测试脚本,split1.sh
和 split2.sh
。
split1.sh
内容如下:
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
|
#!/bin/sh
DataFile="test.data"
if [ ! -e $DataFile ]; then
echo "Fail to open file"
exit 1
fi
F1=0
F2=0
F3=0
F4=0
# 设置分割符
IFS=" "
while read line
do
[ -z "$line" ] && continue
set -- $line
F1=$((F1+$1))
F2=$((F2+$2))
F3=$((F3+$3))
F4=$((F4+$4))
done < $DataFile
echo "F1=$F1"
echo "F2=$F2"
echo "F3=$F3"
echo "F4=$F4"
|
split2.sh
文件内容如下:
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
32
33
|
#!/bin/sh
DataFile="test.data"
if [ ! -e $DataFile ]; then
echo "Fail to open file"
exit 1
fi
F1=0
F2=0
F3=0
F4=0
IFS=" "
while read line
do
[ -z "$line" ] && continue
l1=$(echo $line | cut -f1 -d ' ')
l2=$(echo $line | cut -f2 -d ' ')
l3=$(echo $line | cut -f3 -d ' ')
l4=$(echo $line | cut -f4 -d ' ')
F1=$((F1+$l1))
F2=$((F2+$l2))
F3=$((F3+$l3))
F4=$((F4+$l4))
done < $DataFile
echo "F1=$F1"
echo "F2=$F2"
echo "F3=$F3"
echo "F4=$F4"
|
split1.sh
使用内置方法,split2.sh
使用 cut 工具来处理。在 shell 中运行程序并用 time
命令监控执行时间,会发现速度对比特别显著。
以本人测试电脑为例,内置方法执行输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
$ time ./split1.sh
F1=500500
F2=524500
F3=1528500
F4=1099000
real 0m0.045s
user 0m0.024s
sys 0m0.020s
$ time ./split2.sh
F1=500500
F2=524500
F3=1528500
F4=1099000
real 0m4.603s
user 0m0.004s
sys 0m0.736s
|
粗略看出执行速度相差 100 倍左右。
备注:因为要获取各列数据累加,所以在脚本2中,使用了4次 cut 调用,相当于字符串分割了4此,而脚本1中只使用了一次字符串分割,这种策略也显著增加了执行开销。可以通过使用数组返回 cut 结果来避免多次调用,但是这得看 shell 环境是否支持,某些情况下是不支持数组的,比如 busybox 的 sh
环境。
(完)