运行 bash 脚本中的并行 mongodump
running parallel mongodump in bash script
编者注:这是关于 running a specified number of commands in parallel.
的更笼统问题的后续问题
我正在尝试 运行 这个 mongodb 20 mongodb 服务器的备份脚本。
#!/bin/bash
#daily backup for mongo db's
BACKUP_HOSTS=(example.com staging.example.com prod.example.com example1.com)
#d=$(date +%Y-%m-%dT%H:%M:%S --date "-65 days")
d=$(date +%Y-%m-%dT%H:%M:%S --date "-5 days")
oid=$(mongo --quiet --eval "ObjectId.fromDate(ISODate('$d'))")
cd /data/daily/
rm -r /data/daily/*
TODAY=$(date +"%y-%m-%d")
mkdir "$TODAY"
cd $TODAY
#create subfolders
for HOST in ${BACKUP_HOSTS[@]}
do
mkdir $HOST
done
#extract mongo dumps
echo "$(date '+%Y-%m-%d %H:%M:%S') start retrieving Mongodb backups"
for h in ${BACKUP_HOSTS[@]};do
dbs=`mongo --eval "db.getMongo().getDBNames()" --host $h | grep '"' | tr -d '",' `
for db in $dbs; do
col=`mongo $db --host $h --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' `
for collection in $col; do
xargs -P 0 -n 1 mongodump --host $h -q "{_id:{$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h
done
done
done
但是没有用。
也尝试过:
parallel -P 0 -n 1 mongodump --host $h "{_id:{$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h
但我得到:
bin/bash: -c: line 0: syntax error near unexpected token `('
尝试
mongodump --host $h -q "{_id:{$gt:$oid}}" -d $db -c $collection > /data/daily/$TODAY/$h &
最后的 &
使命令 运行 在后台运行,因此循环的每个命令都会 运行 与前一个命令并行。也看看this。
但是,我建议您总是像这样 "$var"
将您的变量括在双引号中,否则会发生许多异常情况并干扰您的命令的执行。
比如这个错误:
bin/bash: -c: line 0: syntax error near unexpected token `('
这似乎是由您的变量 $collection 具有的一些特殊字符引起的。
因此,它的安全版本是:
mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c "$collection" > /data/daily/"$TODAY"/"$h" &
您可以查看 here 为什么以及何时使用双引号以了解更多详细信息。
尝试以下xargs -P
解决方案:
for h in "${BACKUP_HOSTS[@]}";do
dbs=$(mongo --eval "db.getMongo().getDBNames()" --host "$h" | grep '"' | tr -d '",')
for db in $dbs; do
mongo "$db" --host "$h" --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' |
xargs -P 0 -n 1 mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" --out "/data/daily/$TODAY/$h" -c
done
done
xargs
仅在 stdin 输入上运行,而您的解决方案尝试不提供任何 stdin 输入;上面的解决方案通过管道将集合名称检索 mongo
的结果直接传送到 xargs
.
- 请注意,这假定集合名称既没有嵌入白色 space 也没有嵌入
\
个字符。
-P 0
仅适用于 GNU xargs
,它将 0
解释为:"run as many processes as possible simultaneously" (我不清楚它是如何定义的。
使用 for
循环命令输出通常 脆弱 .
- 只有在 (a) 每个 whitespace-space 分隔的单词都应该被视为它自己的参数并且 (b) 这些单词不包含诸如
*
- 见 this Bash FAQ entry.
请注意所有变量引用(已知仅包含 数字 的变量引用除外)如何 双引号 以确保稳健性.
使用现代命令替换语法 $(...)
而不是旧语法 `...`
,即 preferable.
至于 GNU parallel
命令,尝试以下变体,with stdin input from the collection name-retrieving mongo
命令, 如上:
... | parallel -P 0 -N 1 -q mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c {1} --out "/data/daily/$TODAY/$h"
-N 1
而不是 -n 1
允许您使用占位符 {1}
[= 控制 read-from-stdin 参数在命令行中的放置位置63=]
-q
确保传递带双引号的复杂命令正确传递到 shell。
Shell 变量引用被双引号以确保它们按原样使用。
故障排除 xargs
/ GNU parallel
调用:
xargs
和GNUparallel
都支持-t
(GNU并行:别名--verbose
):
-t
将每个命令行打印到 stderr, 就在它启动之前 .
- 警告:使用
xargs
,输入引号将不会反映在打印的命令中,因此您将无法按指定验证参数边界。
xargs -t
示例:
$ time echo '"echo 1; sleep 1" "echo 2; sleep 2" "echo 3; sleep 1.5"' |
xargs -t -P 2 -n 1 sh -c 'eval ""' -
这会产生如下内容:
sh -c eval "" - echo 1; sleep 1
sh -c eval "" - echo 2; sleep 2
2
1
sh -c eval "" - echo 3; sleep 1.5
3
real 0m2.535s
user 0m0.013s
sys 0m0.013s
注:
命令行缺少参数周围的原始引号,但仍按最初指定执行调用。
它们会在命令启动前立即打印(到 stderr)。
正如所讨论的,命令的输出可能会乱序到达并且不可预测地交错。
整体执行耗时约2.5秒,细分如下:
由于-P 2
,echo 1; ...
和echo 2; ...
命令运行并行,而echo 3; ...
命令,作为3rd 一个最初被阻止了,因为一次允许 运行 的命令不超过 2 个。
1 秒后,echo 1; ...
命令完成,运行ning 并行进程的计数下降到 1,触发执行剩余的 echo 3; ...
命令.
因此,因为最后一个命令在 1 秒后开始,运行 持续 1.5 秒,所以最后一个命令在 ca 之后完成。 2.5 秒(而前 2 个命令分别在 1 秒和 2 秒后完成)。
GNU parallel -t
示例:
$ time echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' |
parallel -P 2 -q -t sh -c 'eval ""' -
sh -c eval\ \"$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"$1\" - echo\ 2\;\ sleep\ 2
1
sh -c eval\ \"$1\" - echo\ 3\;\ sleep\ 1.5
2
3
real 0m2.768s
user 0m0.165s
sys 0m0.094s
注:
因为该命令使用引号来划分参数并且该划分必须传递给sh
,所以还必须指定-q
。
\
-quoting 可能看起来不寻常,但它是正确的 shell 引用并准确反映在幕后调用的命令。
GNU parallel
期望参数默认为 行分隔 ,因此 shell 命令在单独的行上传递使用带有 \n
转义序列的 ANSI C 引号字符串 ($'...'
)。
总体处理时间比 xargs
长,这是您为 GNU parallel
的附加功能和技术基础(Perl 脚本)支付的 - 可能微不足道的 - 价格.
这些附加功能之一是前面提到的输出序列化(分组):第一个命令的输出可预测首先,即使它和第二个命令同时启动;直到 it 完成后才打印第二个命令的输出(这就是第 3 个命令行的诊断打印首先显示的原因)。
GNU parallel
还支持 --dry-run
,其中 仅 打印命令 - 到 stdout - 实际上没有 运行ning 它们。
$ echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' |
parallel -P 2 -q --dry-run sh -c 'eval ""' -
sh -c eval\ \"$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"$1\" - echo\ 2\;\ sleep\ 2
sh -c eval\ \"$1\" - echo\ 3\;\ sleep\ 1.5
只需要在
末尾加一个&
for collection in $cols; do
mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c
"$collection" --out /data/daily/$TODAY/$h
done &
编者注:这是关于 running a specified number of commands in parallel.
的更笼统问题的后续问题我正在尝试 运行 这个 mongodb 20 mongodb 服务器的备份脚本。
#!/bin/bash
#daily backup for mongo db's
BACKUP_HOSTS=(example.com staging.example.com prod.example.com example1.com)
#d=$(date +%Y-%m-%dT%H:%M:%S --date "-65 days")
d=$(date +%Y-%m-%dT%H:%M:%S --date "-5 days")
oid=$(mongo --quiet --eval "ObjectId.fromDate(ISODate('$d'))")
cd /data/daily/
rm -r /data/daily/*
TODAY=$(date +"%y-%m-%d")
mkdir "$TODAY"
cd $TODAY
#create subfolders
for HOST in ${BACKUP_HOSTS[@]}
do
mkdir $HOST
done
#extract mongo dumps
echo "$(date '+%Y-%m-%d %H:%M:%S') start retrieving Mongodb backups"
for h in ${BACKUP_HOSTS[@]};do
dbs=`mongo --eval "db.getMongo().getDBNames()" --host $h | grep '"' | tr -d '",' `
for db in $dbs; do
col=`mongo $db --host $h --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' `
for collection in $col; do
xargs -P 0 -n 1 mongodump --host $h -q "{_id:{$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h
done
done
done
但是没有用。
也尝试过:
parallel -P 0 -n 1 mongodump --host $h "{_id:{$gt:$oid}}" -d $db -c $collection --out /data/daily/$TODAY/$h
但我得到:
bin/bash: -c: line 0: syntax error near unexpected token `('
尝试
mongodump --host $h -q "{_id:{$gt:$oid}}" -d $db -c $collection > /data/daily/$TODAY/$h &
最后的 &
使命令 运行 在后台运行,因此循环的每个命令都会 运行 与前一个命令并行。也看看this。
但是,我建议您总是像这样 "$var"
将您的变量括在双引号中,否则会发生许多异常情况并干扰您的命令的执行。
比如这个错误:
bin/bash: -c: line 0: syntax error near unexpected token `('
这似乎是由您的变量 $collection 具有的一些特殊字符引起的。
因此,它的安全版本是:
mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c "$collection" > /data/daily/"$TODAY"/"$h" &
您可以查看 here 为什么以及何时使用双引号以了解更多详细信息。
尝试以下xargs -P
解决方案:
for h in "${BACKUP_HOSTS[@]}";do
dbs=$(mongo --eval "db.getMongo().getDBNames()" --host "$h" | grep '"' | tr -d '",')
for db in $dbs; do
mongo "$db" --host "$h" --quiet --eval "db.getCollectionNames()" | tr -d ',"[]' |
xargs -P 0 -n 1 mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" --out "/data/daily/$TODAY/$h" -c
done
done
xargs
仅在 stdin 输入上运行,而您的解决方案尝试不提供任何 stdin 输入;上面的解决方案通过管道将集合名称检索mongo
的结果直接传送到xargs
.- 请注意,这假定集合名称既没有嵌入白色 space 也没有嵌入
\
个字符。
- 请注意,这假定集合名称既没有嵌入白色 space 也没有嵌入
-P 0
仅适用于 GNUxargs
,它将0
解释为:"run as many processes as possible simultaneously" (我不清楚它是如何定义的。使用
for
循环命令输出通常 脆弱 .- 只有在 (a) 每个 whitespace-space 分隔的单词都应该被视为它自己的参数并且 (b) 这些单词不包含诸如
*
- 见 this Bash FAQ entry.
- 只有在 (a) 每个 whitespace-space 分隔的单词都应该被视为它自己的参数并且 (b) 这些单词不包含诸如
请注意所有变量引用(已知仅包含 数字 的变量引用除外)如何 双引号 以确保稳健性.
使用现代命令替换语法
$(...)
而不是旧语法`...`
,即 preferable.
至于 GNU parallel
命令,尝试以下变体,with stdin input from the collection name-retrieving mongo
命令, 如上:
... | parallel -P 0 -N 1 -q mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c {1} --out "/data/daily/$TODAY/$h"
-N 1
而不是-n 1
允许您使用占位符{1}
[= 控制 read-from-stdin 参数在命令行中的放置位置63=]-q
确保传递带双引号的复杂命令正确传递到 shell。Shell 变量引用被双引号以确保它们按原样使用。
故障排除 xargs
/ GNU parallel
调用:
xargs
和GNUparallel
都支持-t
(GNU并行:别名--verbose
):
-t
将每个命令行打印到 stderr, 就在它启动之前 .- 警告:使用
xargs
,输入引号将不会反映在打印的命令中,因此您将无法按指定验证参数边界。
xargs -t
示例:
$ time echo '"echo 1; sleep 1" "echo 2; sleep 2" "echo 3; sleep 1.5"' |
xargs -t -P 2 -n 1 sh -c 'eval ""' -
这会产生如下内容:
sh -c eval "" - echo 1; sleep 1
sh -c eval "" - echo 2; sleep 2
2
1
sh -c eval "" - echo 3; sleep 1.5
3
real 0m2.535s
user 0m0.013s
sys 0m0.013s
注:
命令行缺少参数周围的原始引号,但仍按最初指定执行调用。
它们会在命令启动前立即打印(到 stderr)。
正如所讨论的,命令的输出可能会乱序到达并且不可预测地交错。
整体执行耗时约2.5秒,细分如下:
由于
-P 2
,echo 1; ...
和echo 2; ...
命令运行并行,而echo 3; ...
命令,作为3rd 一个最初被阻止了,因为一次允许 运行 的命令不超过 2 个。1 秒后,
echo 1; ...
命令完成,运行ning 并行进程的计数下降到 1,触发执行剩余的echo 3; ...
命令.因此,因为最后一个命令在 1 秒后开始,运行 持续 1.5 秒,所以最后一个命令在 ca 之后完成。 2.5 秒(而前 2 个命令分别在 1 秒和 2 秒后完成)。
GNU parallel -t
示例:
$ time echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' |
parallel -P 2 -q -t sh -c 'eval ""' -
sh -c eval\ \"$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"$1\" - echo\ 2\;\ sleep\ 2
1
sh -c eval\ \"$1\" - echo\ 3\;\ sleep\ 1.5
2
3
real 0m2.768s
user 0m0.165s
sys 0m0.094s
注:
因为该命令使用引号来划分参数并且该划分必须传递给
sh
,所以还必须指定-q
。\
-quoting 可能看起来不寻常,但它是正确的 shell 引用并准确反映在幕后调用的命令。GNU
parallel
期望参数默认为 行分隔 ,因此 shell 命令在单独的行上传递使用带有\n
转义序列的 ANSI C 引号字符串 ($'...'
)。总体处理时间比
xargs
长,这是您为 GNUparallel
的附加功能和技术基础(Perl 脚本)支付的 - 可能微不足道的 - 价格.这些附加功能之一是前面提到的输出序列化(分组):第一个命令的输出可预测首先,即使它和第二个命令同时启动;直到 it 完成后才打印第二个命令的输出(这就是第 3 个命令行的诊断打印首先显示的原因)。
GNU parallel
还支持 --dry-run
,其中 仅 打印命令 - 到 stdout - 实际上没有 运行ning 它们。
$ echo $'echo 1; sleep 1\necho 2; sleep 2\necho 3; sleep 1.5' |
parallel -P 2 -q --dry-run sh -c 'eval ""' -
sh -c eval\ \"$1\" - echo\ 1\;\ sleep\ 1
sh -c eval\ \"$1\" - echo\ 2\;\ sleep\ 2
sh -c eval\ \"$1\" - echo\ 3\;\ sleep\ 1.5
只需要在
末尾加一个&
for collection in $cols; do
mongodump --host "$h" -q "{_id:{$gt:$oid}}" -d "$db" -c
"$collection" --out /data/daily/$TODAY/$h
done &