最近要写一个简单的shell,批量读取一个文本中的数据,插入到db中,并输出插入的条数。读取使用了管道+while方式,问题来了,脚本执行完毕,数据插入正常,但是最后统计的却是插入0行,好奇怪!
撇开我的问题不说,且看下面一个例子。
假设我有一个需求:从data.txt里按行读取一个文件,按格式"[行号]内容"输出,再输出读取的行数。
data.txt:
youth is not a time.
it is a state of mind.
oh, you are right.
有人说,很简单呀,看我啪啪啪,敲了几行:
#!/bin/bash
i=0
cat data.txt | while read line
do
i=`expr $i + 1`
echo “[$i]$line"
done
echo $i
执行的结果却令人大吃一惊:
-----输出结果----------------------
[1]youth is not a time.
[2]it is a state of mind.
[3]oh, you are right.
0
[2]it is a state of mind.
[3]oh, you are right.
0
------------------------------------
为什么是1?明明有3行的,应该是3啊!!!
原因就是:当while遇到管道的时候,循环体会被fork到另一个子shell去执行,此时while中i被改变,然而循环退出时,父shell的i并没变,依旧是0,故而会输出0。
什么?你不信?那么我们试试这个shell:
#!/bin/bash
while true
do
sleep 20
cat data.txt | while read line
do
sleep 1
done
done
言简意赅,让外层循环先跑起来,20s后,内循环开始跑,内循环就是使用管道。
$sh test.sh
立刻查看进程
$ps aux | grep sh
可以看到有如下进程:
----输出结果--------------------------
kyle 22777 0.0 0.0 2216 540 pts/0 S+ 12:30 0:00 sh tread.sh
---------------------------------------
20s后,再次查看:
$ps aux | grep sh
----输出结果--------------------------
kyle 22777 0.0 0.0 2216 540 pts/0 S+ 12:30 0:00 sh tread.sh
kyle 22797 0.0 0.0 2216 540 pts/0 S+ 12:30 0:00 sh tread.sh
---------------------------------------
这下知道了为什么i的值为0了吧。
-------------------------------------我是分割线---------------------------
如何避免以上情况?
答案:不要使用管道。
那用什么?
答案:使用重定向。
#!/bin/bash
i=0
while read line
do
i=`expr $i + 1`
echo "[$i]$line"
done < data.txt
echo $i
再次运行:
-----输出结果----------------------
[1]youth is not a time.
[2]it is a state of mind.
[3]oh, you are right.
3
[2]it is a state of mind.
[3]oh, you are right.
3
------------------------------------
搞定,收工!