Shell以及find的多线程执行

这属于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 "{}"

Linux下的程序限速软件Trickle
Linux内核TCP连接Keep-Alive Timeout的配置
comments powered by Disqus