Shell脚本-分批循环

3

我有一个示例脚本如下,其中我将服务器名称作为逗号分隔的字符串传递,并对每个服务器执行函数。现在我需要批量执行此操作,例如,在第一批中,函数将使用a、b、c、d调用。完成后,它们将取下4个并调用函数,最后它们将调用最后2个。我无法更改“服务器”变量。有任何想法/建议。

servers = a,b,c,d,e,f,g,h,i,j
OIFS=$IFS;
IFS=",";
for server in ${servers}
do
 function1 server
done
for server in ${servers}
do
 function2 server
done
for server in ${servers}
do
 function3 server
done
IFS=$OIFS;

请注意,服务器变量长度不是固定的,它根据加载不同的服务器环境而变化。

只是出于好奇,为什么不使用SGE或Torque来管理处理队列呢? - Gang
3个回答

4
将服务器列表读入数组中,然后使用子字符串扩展(也称为“切片”)。如果我现在理解你对服务器列表长度的关注,你还可以将要调用的函数名称存储在数组中。
IFS=, read -a s <<< "$servers"

bs=4  # batch size
for ((i=0; i<=${#s[@]}; i+=bs)); do
    function1 "${s[@]:i:bs}"
    function2 "${s[@]:i:bs}"
    function3 "${s[@]:i:bs}"
done

有一个小错别字: for ((i=0; i<=${#server_list[@]}; i+=batch_size)); do - Giuseppe Ricupero
谢谢。整个东西太啰嗦了,所以我为了可读性缩短了所有变量名。 - chepner
我认为有一个问题。"${s[@]:i:i+bs}" 应该是 "${s[@]:i:bs}" 吧? - Itération 122442
我相信是这样的;substring扩展的第二个参数是长度,而不是停止位置。 - chepner

1

看起来这需要使用 GNU Parallel。可以像这样:

parallel -d ',' -k -j 4 'func1 {1}; func2 {1}; func3 {1}'  <<< $servers

尝试像这样使用它:
parallel -d ',' -k -j 4 'echo {1}; sleep 2'  <<< $servers
a
b
c
d
e
f
g
h
i
j

如果它们确实是函数(而不是shell脚本或可执行文件),请记得像这样导出它们:
func1() {
  echo Doing it for $1
  sleep 2
  echo Done with $1
}

export -f func1

这个答案被低估了。我本来可能会使用 xargs -n10,但是 parallel -N10 实际上做的事情一样,而且还能并行处理,节省时间! - Julian Mehnle

0

更新 受到@chepner的回答启发,我修改了循环以使其在调用函数时也具有动态性。

function1 () { echo "f1: $@"; }
function2 () { echo "f2: $@"; }
function3 () { echo "f3: $@"; }

servers=a,b,c,d,e,f,g,h,i,j
slice=4

IFS=, read -r -a array <<< "${servers}"

for index in "${!array[@]}"
do
    let "n = (index / slice) + 1" # n = 1 in (0..3), 2 in (4..7), 3 in (8..11), ...
    function${n} ${array[i]}
done

输出

f1:a
f1:b
f1:c
f1:d
f2:e
f2:f
f2:g
f2:h
f3:i
f3:j

显然,这个方法有一个限制:函数必须被称为 function1, function2, ...(实际上函数的名称也可以重新映射,但会失去一些动态性)。

否则,使用更经典的静态循环:

for index in "${!array[@]}"; do
    let "n = (index / slice) + 1"
    ((n == 1)) && function1 ${array[index]}
    ((n == 2)) && function2 ${array[index]}
    ((n == 3)) && function3 ${array[index]}
done

谢谢,但在我的情况下,服务器长度不是固定的,而是根据执行脚本的环境而变化。 - Pinaki Mukherjee
@PinakiMukherjee 这只需要更改所检查的范围即可。 - chepner
1
@GsusRecovery 不要使用 -a;它是 POSIX 标准的扩展,也不建议使用。请改用 [ "$counter" -ge 1 ] && [ "$counter" -le 4 ]。或者更好的方法是,由于您使用非 POSIX 算术语句来增加 counter,因此只需使用 (( counter >= 1 && counter <= 4)) - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接