#!/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