并行执行 Bash 个命令
Parallel execution of Bash commands
我有一个 Bash 脚本,它有一个循环,在循环中有一个 Bash 命令调用另一个 Bash 脚本,该脚本又调用 Python 脚本。
循环中的每个 bash 命令都可以 运行 彼此独立。当我稍后 运行 它在实际数据集上时,执行每个命令需要一些时间。因此,我想利用并并行化这部分脚本。
我花了几天时间查看 Bash 中执行并行执行的选项,同时还让我可以选择要并行化代码的内核数量,这样我就不会淹没服务器.在寻找 GNU 的选项后,xargs -P
在我看来是最合理的,因为我不需要特定的 Bash 版本,它无需安装额外的库就可以工作。然而,我很难让它发挥作用,尽管它看起来很简单。
#!/bin/bash
while getopts i:t: option
do
case "${option}"
in
i) in_f=${OPTARG};;
t) n_threads=${OPTARG};;
esac
done
START=$(date +%s)
class_file=$in_f
classes=( $(awk '{print }' ./$class_file))
rm -r tree_matches.txt
n="${#classes[@]}"
for i in $(seq 0 $n);
do
for j in $(seq $((i+1)) $((n-1)));
do
echo ${classes[i]}" "${classes[j]} >> tree_matches.txt
done
done
col1=( $(awk '{print }' ./tree_matches.txt ))
col2=( $(awk '{print }' ./tree_matches.txt ))
printf "%s[=11=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
n_pairs="${#col1[@]}"
END=$(date +%s)
DIFF=$(( $END - $START ))
echo "Exec time $DIFF seconds"
您可以忽略最初的两个嵌套循环,为了完整起见,我只是粘贴了整个脚本。将要并行化的部分是从脚本末尾算起的第 4 行代码:
printf "%s[=12=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
这将遍历所有对,在我的例子中总共是 1275 对,理想情况下将使用变量 $n_threads
.
与指定数量的线程并行执行 myFunction.sh
但是,我做错了什么,因为该行中的迭代器 k
没有索引我的两个数组 ${classes[k]}
和 ${classes[k]}
.
循环持续迭代 1275 次,但当我回显它们时它只索引两个数组的第一个元素。后来我将该行更改为此行以进行故障排除:
printf "%s[=13=]" {0..1275} | xargs -0 -I k -P $n_threads echo "index" k
它实际上是在每次循环时递增 k
的值,但是当我将该行更改为:
printf "%s[=14=]" {0..1275} | xargs -0 -I k -P $n_threads echo "index" "$((k))"
它正在打印 0,作为 k
的值的 1275 次。我不知道我做错了什么。
我实际上有两个大小相同的向量,它们是 myFunction.sh
脚本的输入。我只希望一个整数索引能够同时对它们进行索引,并使用从这两个向量索引的这两个值调用我的函数。我根据您的建议修改了我的代码如下:
for x in {0..10};
do
printf "%d[=15=]" "$x"; done| xargs -0 -I @@ -P $n_threads sh markerGenes2TreeMatch.sh -1 ${col1[@@]}-2 ${col2[@@]}
但是现在当我执行代码时出现以下错误:
@@: syntax error: operand expected (error token is "@@")
我猜这个索引 @@
仍然是字符串格式。我只希望在循环时生成整数索引并可以并行执行此命令。
对于有问题的行:
printf "%s[=10=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
在 xargs 有机会看到它之前,${classes[k]}
将被 shell 扩展(很可能什么都没有)。
也许您可以重新订购:
for x in {0..1275}; do printf "%s[=11=]" "${classes[$x]}"; done |\
xargs -0 -I @@ -P $n_threads sh myFunction.sh -1 @@ -2 @@
这条线路并没有像您想象的那样工作:
printf "%s[=10=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
发生的事情是 BASH 将首先将 $n_threads
和 ${classes[k]}
之类的内容扩展为字符串,然后调用 xargs
。顺便提一句。 ${classes[k]}
始终是 ""
,因为键 "k"
不在数组 classes
中。尝试 ${classes[$k]}
;然后 BASH 将首先替换变量 k
,然后使用结果在 classes
.
中查找值
也许更好的方法是将 classes
中的值写入文件并将其用作 xargs
的输入。您可能必须更改 myFunction.sh
以接受单个参数(= 一行输入)并在脚本中将其拆分。
使用 GNU Parallel 你可能会做:
classes=( $(awk '{print }' ./$class_file))
parallel markerGenes2TreeMatch.sh -1 {=1 'if($arg[1] ge $arg[2]) { skip() }' =} -2 {2} ::: ${classes[@]} ::: ${classes[@]}
或:
parallel --plus markerGenes2TreeMatch.sh -1 {1choose_k} -2 {2choose_k} ::: ${classes[@]} ::: ${classes[@]}
那么可以跳过整代tree_match.txt,$col1/$col2.
使用 parallel --embed
将 GNU Parallel 直接包含在您的脚本中,因此您没有外部依赖项。
我有一个 Bash 脚本,它有一个循环,在循环中有一个 Bash 命令调用另一个 Bash 脚本,该脚本又调用 Python 脚本。
循环中的每个 bash 命令都可以 运行 彼此独立。当我稍后 运行 它在实际数据集上时,执行每个命令需要一些时间。因此,我想利用并并行化这部分脚本。
我花了几天时间查看 Bash 中执行并行执行的选项,同时还让我可以选择要并行化代码的内核数量,这样我就不会淹没服务器.在寻找 GNU 的选项后,xargs -P
在我看来是最合理的,因为我不需要特定的 Bash 版本,它无需安装额外的库就可以工作。然而,我很难让它发挥作用,尽管它看起来很简单。
#!/bin/bash
while getopts i:t: option
do
case "${option}"
in
i) in_f=${OPTARG};;
t) n_threads=${OPTARG};;
esac
done
START=$(date +%s)
class_file=$in_f
classes=( $(awk '{print }' ./$class_file))
rm -r tree_matches.txt
n="${#classes[@]}"
for i in $(seq 0 $n);
do
for j in $(seq $((i+1)) $((n-1)));
do
echo ${classes[i]}" "${classes[j]} >> tree_matches.txt
done
done
col1=( $(awk '{print }' ./tree_matches.txt ))
col2=( $(awk '{print }' ./tree_matches.txt ))
printf "%s[=11=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
n_pairs="${#col1[@]}"
END=$(date +%s)
DIFF=$(( $END - $START ))
echo "Exec time $DIFF seconds"
您可以忽略最初的两个嵌套循环,为了完整起见,我只是粘贴了整个脚本。将要并行化的部分是从脚本末尾算起的第 4 行代码:
printf "%s[=12=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
这将遍历所有对,在我的例子中总共是 1275 对,理想情况下将使用变量 $n_threads
.
myFunction.sh
但是,我做错了什么,因为该行中的迭代器 k
没有索引我的两个数组 ${classes[k]}
和 ${classes[k]}
.
循环持续迭代 1275 次,但当我回显它们时它只索引两个数组的第一个元素。后来我将该行更改为此行以进行故障排除:
printf "%s[=13=]" {0..1275} | xargs -0 -I k -P $n_threads echo "index" k
它实际上是在每次循环时递增 k
的值,但是当我将该行更改为:
printf "%s[=14=]" {0..1275} | xargs -0 -I k -P $n_threads echo "index" "$((k))"
它正在打印 0,作为 k
的值的 1275 次。我不知道我做错了什么。
我实际上有两个大小相同的向量,它们是 myFunction.sh
脚本的输入。我只希望一个整数索引能够同时对它们进行索引,并使用从这两个向量索引的这两个值调用我的函数。我根据您的建议修改了我的代码如下:
for x in {0..10};
do
printf "%d[=15=]" "$x"; done| xargs -0 -I @@ -P $n_threads sh markerGenes2TreeMatch.sh -1 ${col1[@@]}-2 ${col2[@@]}
但是现在当我执行代码时出现以下错误:
@@: syntax error: operand expected (error token is "@@")
我猜这个索引 @@
仍然是字符串格式。我只希望在循环时生成整数索引并可以并行执行此命令。
对于有问题的行:
printf "%s[=10=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
在 xargs 有机会看到它之前,${classes[k]}
将被 shell 扩展(很可能什么都没有)。
也许您可以重新订购:
for x in {0..1275}; do printf "%s[=11=]" "${classes[$x]}"; done |\
xargs -0 -I @@ -P $n_threads sh myFunction.sh -1 @@ -2 @@
这条线路并没有像您想象的那样工作:
printf "%s[=10=]" {0..1275} | xargs -0 -I k -P $n_threads sh myFunction.sh -1 ${classes[k]} -2 ${classes[k]}
发生的事情是 BASH 将首先将 $n_threads
和 ${classes[k]}
之类的内容扩展为字符串,然后调用 xargs
。顺便提一句。 ${classes[k]}
始终是 ""
,因为键 "k"
不在数组 classes
中。尝试 ${classes[$k]}
;然后 BASH 将首先替换变量 k
,然后使用结果在 classes
.
也许更好的方法是将 classes
中的值写入文件并将其用作 xargs
的输入。您可能必须更改 myFunction.sh
以接受单个参数(= 一行输入)并在脚本中将其拆分。
使用 GNU Parallel 你可能会做:
classes=( $(awk '{print }' ./$class_file))
parallel markerGenes2TreeMatch.sh -1 {=1 'if($arg[1] ge $arg[2]) { skip() }' =} -2 {2} ::: ${classes[@]} ::: ${classes[@]}
或:
parallel --plus markerGenes2TreeMatch.sh -1 {1choose_k} -2 {2choose_k} ::: ${classes[@]} ::: ${classes[@]}
那么可以跳过整代tree_match.txt,$col1/$col2.
使用 parallel --embed
将 GNU Parallel 直接包含在您的脚本中,因此您没有外部依赖项。