bash:使用文件列表限制for循环中的子shell
我一直在尝试让for循环同时运行一系列命令,并试图通过子shell来实现。我设法拼凑了下面的脚本进行测试,它似乎工作正常bash:使用文件列表限制for循环中的子shell,bash,for-loop,subshell,Bash,For Loop,Subshell,我一直在尝试让for循环同时运行一系列命令,并试图通过子shell来实现。我设法拼凑了下面的脚本进行测试,它似乎工作正常 #!/bin/bash for i in {1..255}; do ( #commands )& done wait 唯一的问题是,我的实际循环将是文件中的i,然后它崩溃了,我想这是因为它启动了太多的子shell,无法处理。所以我补充说 #!/bin/bash for i in files*; do ( #commands )&am
#!/bin/bash
for i in {1..255}; do
(
#commands
)&
done
wait
唯一的问题是,我的实际循环将是文件中的i,然后它崩溃了,我想这是因为它启动了太多的子shell,无法处理。所以我补充说
#!/bin/bash
for i in files*; do
(
#commands
)&
if (( $i % 10 == 0 )); then wait; fi
done
wait
现在失败了。有人知道怎么解决这个问题吗?或者使用不同的命令来限制子shell的数量,或者为$i提供一个数字
干杯您会发现使用
作业
计算作业数很有用。e、 g:
wc -w <<<$(jobs -p)
wc-w明确定义计数器
#!/bin/bash
for f in files*; do
(
#commands
)&
(( i++ % 10 == 0 )) && wait
done
wait
无需初始化i
,因为第一次使用它时它将默认为0。也无需重置该值,因为i=10、20、30等的i%10
将为0。xargs/parallel
另一个解决方案是使用为并发性设计的工具:
printf '%s\0' files* | xargs -0 -P6 -n1 yourScript
-P6
是xargs
将启动的最大并发进程数。如果你愿意的话,就定为10
我建议使用xargs
,因为它可能已经出现在您的系统中。如果您想要一个真正健壮的解决方案,请查看
数组中的文件名
对于您的问题的另一个明确答案:获取计数器作为数组索引
files=( files* )
for i in "${!files[@]}"; do
commands "${files[i]}" &
(( i % 10 )) || wait
done
(复合命令周围的括号并不重要,因为作业的后台处理与使用子shell的效果相同。)
作用
只是语义不同:
simultaneous() {
while [[ $1 ]]; do
for i in {1..11}; do
[[ ${@:i:1} ]] || break
commands "${@:i:1}" &
done
shift 10 || shift "$#"
wait
done
}
simultaneous files*
如果你有Bash≥4.3,您可以使用等待-n
:
#!/bin/bash
max_nb_jobs=10
for i in file*; do
# Wait until there are less than max_nb_jobs jobs running
while mapfile -t < <(jobs -pr) && ((${#MAPFILE[@]}>=max_nb_jobs)); do
wait -n
done
{
# Your commands here: no useless subshells! use grouping instead
} &
done
wait
这样做的好处是,我们对完成工作所需的时间不做任何假设。一个新的工作一旦有空间就开始了。此外,它都是纯Bash,因此不依赖外部工具,而且(可能更重要的是),您可以使用您的Bash环境(变量、函数等)而不导出它们(数组无法轻松导出,因此可能会成为一个巨大的专业版)。您对文件做了什么?这“然后它就崩溃了”很难让人相信。我很确定它会产生一些错误信息。我喜欢这个。更便宜+1近一点看,我认为这不够方便,因为你可以有一个I==10
,尽管后台作业的数量可以少于10个(它们可能会完成)。如果目标是让核心尽可能忙,我会使用类似于parallel
的作业调度程序,而不是在bash
中从头编写一个。这只是一种防止太多作业同时启动的方法,而不是让尽可能多的作业保持运行。在bash
4.3中,您可以使用wait-n
等待任何单个作业完成,然后再启动下一个作业。但是,这会受到竞争条件的影响(我认为是不可避免的),在这种情况下,wait-n
在一个作业完成后被调用,这可能会导致一段时间内可以添加一个新作业,但我们正在等待另一个作业完成。如果您真的想让所有核心保持忙碌,请启动比一次可以运行的进程更多的进程,让操作系统来做调度。这对于I/O绑定的作业尤其可取,因为在其他进程可以运行时,这些作业可能处于空闲状态。另一个用于wait-n
的插件可以更快地启动新作业。
#!/bin/bash
max_nb_jobs=10
for i in file*; do
# Wait until there are less than max_nb_jobs jobs running
while mapfile -t < <(jobs -pr) && ((${#MAPFILE[@]}>=max_nb_jobs)); do
wait -n
done
{
# Your commands here: no useless subshells! use grouping instead
} &
done
wait
#!/bin/bash
set -m
max_nb_jobs=10
sleep_jobs() {
# This function sleeps until there are less than $1 jobs running
local n=$1
while mapfile -t < <(jobs -pr) && ((${#MAPFILE[@]}>=n)); do
coproc read
trap "echo >&${COPROC[1]}; trap '' SIGCHLD" SIGCHLD
[[ $COPROC_PID ]] && wait $COPROC_PID
done
}
for i in files*; do
# Wait until there are less than 10 jobs running
sleep_jobs "$max_nb_jobs"
{
# Your commands here: no useless subshells! use grouping instead
} &
done
wait