我在下面有一个简单的脚本,并在并行中流了压缩MySQL转储到Amazon S3存储桶中:
#!/bin/bash
COMMIT_COUNT=0
COMMIT_LIMIT=2
for i in $(cat list.txt); do
echo "$i "
mysqldump -B $i | bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2 &
(( COMMIT_COUNT++ ))
if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]; then
COMMIT_COUNT=0
wait
fi
done
if [ ${COMMIT_COUNT} -gt 0 ]; then
wait
fi
输出看起来像这样:
database1
database2
duration: 2.311823213s
duration: 2.317370326s
是否有一种方法可以在每个转储的一行上打印它?
database1 - duration: 2.311823213s
database2 - duration: 2.317370326s
在这种情况下,echo -n
开关无济于事。
编辑:5月6日15:17:29 BST 2015
我能够基于接受的答案获得预期的结果:
echo "$i -" $(mysqldump -B $i| bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2 2>&1) &
- 但是,在子壳中运行的命令不是将退出状态返回到父壳,因为它是并行运行的,因此我无法验证它是否成功或失败。
我认为此命令会做您想要的:
echo "$i -" `(mysqldump -B $i | bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2) 2>&1` &
或,使用$()
代替Backticks:
echo "$i -" $( (mysqldump -B $i| bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2) 2>&1 ) &
echo
命令将等待mysqldump ..
结果完成,然后再尝试与$i
一起打印。子壳( … )
和错误重定向2>&1
确保错误消息也进入回声输出。$(
之后的空间是必要的,因为没有空间的$((
是一个不同的特殊操作 - 算术扩展。
感谢您的所有帮助,但我认为我终于找到了最佳解决方案。
基本上,我使用xargs
格式化输出,因此每个条目(转储名称 持续时间)都在一行上。我还将作业规范添加到wait
命令中以获取退出状态:
男人bash
等待[n ...] 等待每个指定的过程并返回其终止状态。每个n可以是过程ID或工作规范;如果工作规格是 给出,该作业管道中的所有过程都在等待。如果是 未给出,所有当前活跃的儿童流程都在等待,并且 返回状态为零。如果n指定不存在的过程或 作业,返回状态为127。否则,返回状态是 最后一个过程或工作的退出状态等待。
测试:
# sh -c 'sleep 5; exit 1' &
[1] 29970
# wait; echo $?
0
# sh -c 'sleep 5; exit 1' &
[1] 29972
# wait $(jobs -p); echo $?
1
最终脚本:
#!/bin/bash
COMMIT_COUNT=0
COMMIT_LIMIT=2
while read -r i; do
mysqldump -B $i | bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2 |& xargs -I{} echo "${DB} - {}" &
(( COMMIT_COUNT++ ))
if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]; then
COMMIT_COUNT=0
wait $(jobs -p)
fi
done < list.txt
if [ ${COMMIT_COUNT} -gt 0 ]; then
wait $(jobs -p)
fi
if [ $? -ne 0 ]; then
echo "ERROR: Backups failed"
exit 1
fi
扩展答案,要在失败时立即退出脚本,您必须将背景过程的pid保存在数组中。在您的时循环中添加 pids[COMMIT_COUNT]=$!
之后的 mysqldump
命令。
然后,您可以编写一个函数以循环在所有这些pid上,如果其中一个失败,则退出:
wait_jobs() {
for pid in "${pids[@]}"; do
wait ${pid}
if [ $status -ne 0 ]; then
echo "ERROR: Backups failed"
exit 1
fi
done
}
在脚本中调用此功能,而不是wait $(jobs -p)
。
注释
您可以用for循环中的jobs -p
替换PID数组,但是您将无法获取在呼叫循环之前完成的作业的PID。
上面的wait_jobs()
功能不能在子壳中使用,exit 1
调用只会终止子壳。
完整的脚本:
#!/bin/bash
COMMIT_COUNT=0
COMMIT_LIMIT=2
wait_jobs() {
for pid in "${pids[@]}"; do
wait ${pid}
if [ $status -ne 0 ]; then
echo "ERROR: Backups failed"
exit 1
fi
done
}
while read -r i; do
mysqldump -B $i | bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2 |& xargs -I{} echo "${DB} - {}" &
# save the pid of the background job so we can get the
# exit status with wait $pid later
pids[COMMIT_COUNT]=$!
(( COMMIT_COUNT++ ))
if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]; then
COMMIT_COUNT=0
wait_jobs
fi
done < list.txt
wait_jobs
关于您有关退出状态的其他问题,让我写另一个答案。因为$()
将运行一个子壳,所以我认为不像普通命令那样将退出状态返回到主壳。但是可以将退出状态写入以后要检查的文件。请在下面尝试命令。它将创建称为status-$i.txt
的文件,其中包含两行。一个用于mysqldump
,另一个用于gof3r
。
e="status-$i.txt"
echo -n > $e
echo "$i -" $(
( mysqldump -B $i 2>&1; echo m=$? >> $e )
| bzip2 -zc
| ( gof3r put -b s3bucket -k $i.sql.bz2 2>&1; echo g=$? >> $e )
) &
您也可能需要在脚本开始时清理所有status-*.txt
文件。
我将单独的功能控制所有过程,然后在后台运行此功能,而不是运行mysqldump。
通过执行此操作,您将同时使用几个过程,同时您可以控制MySqlDump,因为它同时运行
#!/bin/bash
do_job(){
param=$1
echo job $param started... >&2 # Output to stderr as stdout is grabbed
sleep $[$RANDOM/5000]
echo $RANDOM # Make some output
[ $RANDOM -ge 16383 ] # Generate exit code
}
control_job() {
param=$1
output=`do_job $param`
exit_code=$?
echo $1 printed $output and exited with $exit_code
}
JOBS_COUNT=0
JOBS_LIMIT=2
for i in database1 database2 database3 database4; do
control_job $i &
(( JOBS_COUNT++ ))
if [ $JOBS_COUNT -ge $JOBS_LIMIT ]; then
(( JOBS_COUNT-- ))
wait -n 1 # wait for one process to exit
fi
done
wait # wait for all processes running
此处使用do_job
代替您的mysqldump Pipline。顺便说一句,这里有一个很小的进步。您可能不想在达到限制时等待所有产卵过程。等待任意一个就足够了。这就是wait -n 1
做的
您尝试与脚本进行并行化。我建议不要重新发明轮子,而要使用经过久经考验的工具:GNU平行。教程很大:http://www.gnu.org/software/parallel/parallel_tutorial.html
它具有不同的作业选项,可以返回以退出值返回!= 0:第一个错误或继续工作直到结束。
与OP脚本平行的GNU的优点之一是,它一旦完成后立即开始第三个作业。
未经测试等
#!/bin/sh
COMMIT_COUNT=0
COMMIT_LIMIT=2
_dump() {
# better use gzip or xz. There's no benefit using bzip2 afaict
output="$(mysqldump -B "$1" | bzip2 -zc | gof3r put -b s3bucket -k "$1.sql.bz2" 2>&1)"
[ "$?" != 0 ] && output="failed"
printf "%s - %sn" "$1" "$output"
}
while read i; do
_dump "$i" &
(( COMMIT_COUNT++ ))
if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]; then
COMMIT_COUNT=0
wait
fi
done < list.txt
wait