while read loop

#!/bin/bash
res=0
cut -d: -f3 /etc/passwd | while read line
do
	if [ $line -ge 500 ]
	then
		let res++
		echo $res
	fi
done
echo $res

脚本实现的功能就是统计UID大于500的项之和。下面是输|出结果
1
2
3
4
5
6
7
8
9
10
0

为什么最后res又变成0了呢?原因就是管道的调用!

shell执行“|”的时候,父进程调用pipe函数,然后返回两个文件描述符,管道的读描述符写描述符,这两个

描述符号和父进程原有的标准输入(0),标准输出(1),错误输出(2)并存。接着父进程会为管道中的

每条命令都产生一个子进程,产生子进程的之后再通过exec调用相应的命令程序覆盖子进程。管道其实就是

内核的一段缓冲区,因为是内核的缓冲区,所以没有文件的交互,效率比较高。管道建立好之后,两边的子

进程一边开始准备写(通过文件描述符的修改实现重定向),另一边开始准备读取(同上)。等一切准备就

绪,写端就开始写一旦缓冲区满了或者命令执行结束,读端就开始读。

现在再来看看上面的程序,res 是在父进程中定义的,调用管道的时候会产生子进程,子进程继承父进程的变

量,但也只是一份拷贝,所以在子进程里无论 res 怎么变化,到最后子进程exit,父进程是不受影响的。所以

最后的输出还是0。

那怎么解决面的问题呢?

1.通过tmp文件,但是效率低了

#!/bin/bash
export i=0
cut -d: -f3 /etc/passwd  > tmp.txt
while read line
do
if [ $line -ge 500 ];then
	let i++
	echo $i
fi
done < tmp.txt
echo $i

2.避免管道,用for实现
#!/bin/bash

res=0
var=`cut -d: -f3 /etc/passwd`
for word in $var
do
	if [ $word -ge 500 ];then
		res=$((res + 1))
	fi
done

echo $res

标签:Linux, Shell

评论已关闭