这属于Shell的高级技巧了,我们可能需要在 bash 中并发 wget rsync 文件,下面就讨论一下这个问题。
首先从简单的单线程开始:
1$ for i in $(seq 1 2); do echo $i; done
21
32
可以看到是顺序执行的,下面变多线程:
1$ for i in $(seq 1 2); do echo $i & done
2[1] 245505
31
4[2] 245506
52
6[1] Done echo $i
7[2] Done echo $i
可以看到我们只把 ; 号改成了 & 号,程序就变成了多线程执行。
区别在于 ; 号会等待之前的命令执行完毕再执行下一条,而 & 不等待,直接继续执行下一条;相当于后台运行了前一条命令。
下面说说 find 的单线程和多线程:
find 的 exec 用法
1$ find /path [args] -exec [cmd] {} \;
- {} 占位符号,存放find找到的记录
- ; 对于每一条找到的单独记录,执行的cmd是一条一条单独执行的
- 执行的顺序如下: cmd result1; cmd result2; …; cmd result N
1$ find /path [args] -exec [cmd] {} \+
- {} 占位符号,存放find找到的记录
- + 对于找到的所有记录,执行的cmd是合并了所有记录集执行的
- 执行顺序如下: cmd result1 result2 … result N
多个exec可以串起来:
1$ find /tmp/dir1/ -type f -exec grep howtouselinux {} \; -exec echo {} \; | sed 's/howtouselinux/deep/g'
至此,find 也还是单线程执行的,并没有并发。
find 要并发,就只能跟 xargs 结合在一起:
xargs 通常配合管道使用,将前面命令产生的参数,逐个传入后续命令,作为参数。xargs 传来的参数,默认位于 xargs 后面命令的最后,如果要改变位置,需要用**-I**参数。xargs 如果不带命令,缺省是 echo
-
-d 分隔符
1$ echo -e "a\tb\tc" | xargs -d "\t" echo 2a b c
-
-I{} 指定占位符,-I %那就是 % 替代从之前管道取得的参数
1$ find . -type d | xargs -I % -0 rsync -auvPR % 192.168.1.38::new/
-
-0 跟find的-print0配合,find命令有一个特别的参数-print0,指定输出的文件列表以null分隔。然后,xargs命令的-0参数表示用null当作分隔符。
1$ find ./new -mindepth 6 -maxdepth 6 -type d -print0 | xargs -I % -0 rsync -auvPR % 172.18.34.38::new/
-
-P 最大并发线程数,下面是并发30线程
1$ find ./new -mindepth 6 -maxdepth 6 -type d -print0 | xargs -P 30 -I % -0 rsync -auvPR % 172.18.34.38::new/
-
-n 选项限制单个命令行的参数个数,下面是 rsync 一行命令传带60个文件,30个进程那就是30个 rsync,每个 rsync 同时传60个文件。
1$ find ./new -mindepth 6 -maxdepth 6 -type d -print0 | xargs -P 30 -n 60 -I % -0 rsync -auvPR % 172.18.34.38::new/ 2 3$ echo {0..10} | xargs -I{} -n 2 40 1 52 3 64 5 76 7 88 9 910
使用 bash -c 并发的例子:
1$ time for i in $(seq 1 5); do echo $[$RANDOM % 5 + 1]; done | xargs -I{} echo "sleep {}; echo 'Done! {}'" | xargs -P5 -I{} bash -c "{}"