shell虽然执行效率差,但优点,是所用即所得,不用移植,基本上所有linux都通用,基本不用额外安装程序。s
当然运用的好的话,效率也不会太差。比如,尽可能的减少读写文件,减少无名管道“|”的使用,大量的循环尽可能用awk里完成,还有多线程的使用。
多线程,就是让shell同时执行多行命令。
shell里并不能同时接收多条命令,也就是,必须逐行逐条执行,想要实现执行多行,可以把前一条命令置入后台,后面的命令就可以不必等待前一条的完毕,而是立即执行。
# 后台执行在命令行结尾加”空格&“
# while true;do sleep 5s;done &
```
# while true;do sleep 5s;done
^C
# while true;do sleep 5s;done &
#
```
这样就可以很开心的多行执行命令了:# cat proc.sh
```
#!/bin/bash
for i in $(seq 5);do
echo "While A 01 : $i" >>proc_test.txt
echo "While A 02 : $i" >>proc_test.txt
echo "While A 03 : $i" >>proc_test.txt
sleep 1s
done &
for j in $(seq 5);do
echo " While B 01 : $j" >>proc_test.txt
echo " While B 02 : $j" >>proc_test.txt
echo " While B 03 : $j" >>proc_test.txt
sleep 0.5s
done &
wait
```
预想的是向文件proc_test.txt每次输入三行,而实际。。。。。
```
cat proc_test.txt
# sh proc.sh
# cat proc_test.txt
While A 01 : 1
While A 02 : 1
While B 02 :
While A 03 : 1
While B 03 :
While B 02 :
While B 03 :
While A 01 : 2
While A 02 : 2
While A 03 : 2
While B 02 :
While B 03 :
While B 02 :
While B 03 :
While A 01 : 3
While A 02 : 3
While A 03 : 3
While B 02 :
While B 03 :
While A 01 : 4
While A 02 : 4
While A 03 : 4
While A 01 : 5
While A 02 : 5
While A 03 : 5
#
```
不但行是乱的,列也出现了混乱。。出现这种情况,是因为在while A输出时,while B也在输出,当对同一个文件写入时,A B的内容就被混编写入了文件。
这种混乱的多线程,并不是我们想要的。
必须得让他们有顺序的输出。
管道!这时就该他发挥作用了。
管道就像名字一样,是一个运载数据流的通道,可以像水管一样控制它的开关和流速。
mkfifo用于建立管道文件。
```
# mkfifo t.fifo
# ls -l t.fifo
prw-r--r-- 1 root root 0 Nov 13 15:44 t.fifo
#
```
建立个名为 t.fifo的管道文件
管道可以像普通文件一样的读写操作,但是有容量限制,一般是1024字节
向管道写入时,必须同时读取,否则,写入端处于停滞状态。反之也是,
从管道读取时,如果管道内没有数据,读取端也会处于停滞状态。
这里就是关键。看到”停滞“了吗,如果当while A写入文件时,让其它while写入这个文件的命令行处于停滞,那么A写入的内容就不会被B打乱了。
这样基本的概念就实现了。
因为管道文件读写必须同步,所以这里用到描述符
exec 描述符<>管道
<表示读操作,>表示写操作
echo >&100 ,向100里输入一行,代表一个执行令牌
read <&100 ,从100里读取一个令牌,也可用read -u100,如果没有则停滞,等待新的令牌输入
echo >&100 ,while里这条,三行一组输出完成时,回交令牌。
{}把多行命令分成一组
注:描述符只在当前shell里,或者说,当前进程及其子进程有效,
而管道文件,是整个系统有效。所以在定义完描述符,可以 # rm t.fifo
如果读写不同步时
每三行一组的输出基本实现了
```
#cat proc_test.txt
```
最后的小问题,当echo $i >>file.txt时,是处于后台,所以{}外的$i 不会影响{}里的$i 的值。
上面已基本完成一个多线程的规范输出,但如果whileAB都是无限循环,且后台部分要更耗时,那么shell下的while进程就会不断的增加,增加到无限多。最终系统承载不住,就会当机。
这并不是我们希望发生的,那么就应该限制一下,后台的最大数量。
解决方法很多,当然你也可以用记数循环+循环的套用来实现、
在这里,我们要用管道的读写特性来实现后台数量的限制。
和前面的有序输出是同一个原理,有序输出,每次只能读取一个执行令牌,也就是单车道。
那么我们为后台进程新开设个多车道,就暂定为5道并开。
```
#!/bin/bash
mkfifo t.fifo # 建立管道文件
exec 100<>t.fifo #定义描述符
# 1车道
echo >&100
mkfifo proc.fifo proc2.fifo #定义2两管道文件
exec 101<>proc.fifo #定义描述符
exec 102<>proc.fifo #定义描述符
# 开通5车道
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
echo |tee >&101 >&102
# 这里定义个管道,用于,外部向脚本发送指令
mkfifo manager.fifo
exec 1000<manager.fifo
for i in $(seq 5);do
read <&101 #如果没有补充,最多读取5次,否则停滞等待
{
read <&100
echo "While A 01 : $i" >>proc_test.txt
echo "While A 02 : $i" >>proc_test.txt
echo "While A 03 : $i" >>proc_test.txt
echo >&100 # 补充1次
} &
echo >&101
sleep 1s
done &
for j in $(seq 5);do
read <&101 #如果没有补充,最多读取5次,否则停滞等待
{
read <&100
echo " While B 01 : $j" >>proc_test.txt
echo " While B 02 : $j" >>proc_test.txt
echo " While B 03 : $j" >>proc_test.txt
echo >&101 # 补充1次
} &
sleep 0.5s
done &
while true;do
read -u1000 manager
# 试试在脚本执行时,再开启个终端,并echo "hello shell" >manager.fifo
# 脚本会接收到这个信息,并打印出来。
echo -e "\t\t这里是从脚本外得到的输入内容 : $manager"
done
wait
```
如此这般,双后台的5线程并行就设置好了
注:echo |tee >&101 >&102 相当于
echo >&101 ;echo >&102
tee 把输出的内容分流给2个指向tee -a file是追加模式
最后附加了,从脚本外向脚本内传送信息。
第一次写东西,有点乱。
下一篇 ----> 定义管道的通用脚本