首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

并发 Shell 命令队列

摘要

How to run shell commands in parallel while limiting their concurrency. I had a use case recently where I wanted to speed up some Bash processing that I was doing, but still keep concurrency constrain...

shell the commands queue that Bash this concurrency and You
2026-11-14 1 阅读 约9分钟阅读 speckx
分享:
字号:
如何并行运行 shell 命令,同时限制其并发性。我最近有一个用例,我想加快我正在进行的一些 Bash 处理速度,但仍然保持并发限制。所以我想知道:是否有一种直接的方法在 Bash 中创建线程池?答案是:是的,但是需要一些工作才能实现半弹性和便携。您可能会问:为什么要在 Bash 中这样做?使用并发控制较差的语言似乎很愚蠢。你问这个问题是对的,我的答案是:因为我喜欢 Bash,并且我想要挑战。但使用其他语言可能会更好,例如 Python 。下面的代码创建了一个命令队列,该队列接受要执行的 shell 命令。您不需要预先知道要执行的所有命令,您可以随着时间的推移向其提交命令。 1 # 启动该 shell 的命令队列。 2 # @param {number} $1 一次运行的最大作业数 3 queue_start () { 4 # 验证并行度是否为 xargs 的有效限制 5 if [[ ! ${1:-} =~ ^[1-9][0-9]*$ ]]; then 6 echo "queue_start: size 必须是正整数,got ' ${1:-} '" >&2 7 return 1 8 fi 9 10 # 关闭此 shell 的继承或更早版本的副本(不是父级的) 11 # 命令队列的文件描述符 12 if [[ -n ${_queue_fd:-} ]]; then 13 eval "exec ${_queue_fd} >&-" 14 unset _queue_fd _queue_pid 15 fi 16 17 # 查找 3 到 9 中第一个未使用的 fd (zsh 的重定向最大值) 18 local fd=3 19 while { : <& " ${fd} " ; 2>/dev/null;做 fd=$(( fd + 1 ));完成 20 if [[ ${fd} -gt 9 ]]; then 21 echo "queue_start: no free fd from 3 to 9" >&2 22 return 1 23 fi 24 25 # 在私有临时目录中创建一个 FIFO 队列文件 26 local dir 27 dir =$( mktemp -d " ${TMPDIR:-/tmp} /queue.XXXXXX" ) 28 mkfifo " ${dir} /queue" 29 30 # 查找执行命令的 shell(当前 shell) 31 local shell 32 shell=$(ps -o comm = -p "$$" ) 33 shell= ${shell#-} 34 35 # 在后台启动 xargs,从 36 # 队列文件中读取以 NUL 分隔的命令。通过关闭作业控制和 37 # 丢弃块的 stderr 来隐藏作业消息。 xargs 通过 38 # 队列文件的 fd 上的副本获取真正的 stderr,直到文件被打开。 39 eval "exec ${fd} >&2" 40 local opts=$- 41 set +m 42 { 43 # shellcheck disable=SC2016 44 xargs -0 -n 1 -P " $1 " " ${shell} " -c ' 45 # 输出被捕获而不是流式传输,以便命令输出保持独立 46 out=$(eval -- "$1" &1); rc=$? [第 47 章] printf "%s\n" "${out}" 48 # 将非零退出代码强制为 1,因为 255 代码会停止 xargs 49 [ "${rc}" -eq 0 ] || { printf "退出 %d: %s\n" "${rc}" "$1" >&2; 1号出口; 50 }' _ < " ${dir} /queue" 2>& " ${fd} " & 51 _queue_pid=$! 52 否认 || : 53 } 2>/dev/null 54 if [[ ${opts} == *m* ]];然后设置-m; fi 55 56 # 打开队列文件进行写入,等待 xargs 打开它进行读取 57 # 两端都打开,可以安全地删除该文件 58 eval "exec ${fd} >\"\${dir}/queue\"" 59 _queue_fd= ${fd} 60 rm -rf " ${dir} " 61 62 # 注册一个退出处理程序来终止此 PID孩子们,确保 zsh 的行为类似于 bash 63 setopt localoptions posixtraps 2>/dev/null || : 64 trap _queue_stop EXIT 65 if [[ $- != *i* ]]; then 66 trap 'exit 129' HUP 67 trap 'exit 130' INT 68 trap 'exit 143' TERM 69 fi 70 } 71 72 # 将命令放入该 shell 的命令队列中。 73 # @param {string} $1 运行命令 74 queue_submit () { 75 if [[ -z ${_queue_fd:-} ]]; then 76 echo "queue_submit: 此 shell 中没有启动命令队列" >&2 77 return 1 78 fi 79 # 将命令写入队列文件,如果已满则阻塞 80 # 使用 printf 进程,这样如果 xargs 已退出,printf 将被杀死,而不是这个 shell 81 if [[ $- == *i* ]]; then 82 # 在前台写入命令,因为交互式 shell 不会 83 # 有任何可能因队列已满而延迟的 TERM/HUP/INT 陷阱 84 env printf '%s\0' " $1 " >& " ${_queue_fd} " 85 else 86 # 在后台写入命令,以便完整队列不会延迟任何 TERM/HUP/INT 陷阱 87 env printf '%s\0' " $1 " >& " ${_queue_fd} " & 88 等待 "$!" 89 菲 || { 90 echo "queue_submit:无法对命令进行排队;命令队列可能已停止" >&2 91 return 1 92 } 93 } 94 95 # 关闭队列文件,以便 xargs 完成其拥有的作业,然后等待它退出 96 queue_wait () { 97 if [[ -z ${_queue_fd:-} ]];然后 98 echo "queue_wait: 此 shell 中没有启动命令队列" >&2 99 return 1 100 fi 101 eval "exec ${_queue_fd} >&-" 102 while kill -0 " ${_queue_pid} " 2>/dev/null;睡0.1; done 103 unset _queue_fd _queue_pid 104 } 105 106 # 停止给定 PID 以下的所有进程。 107 # @param {nu
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱