我已经在下面使用这个简单的脚本将压缩的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
开关在这种情况下无效。
编辑:2015年5月6日星期三15:17:29 BST 2015
我能够根据公认的答案达到预期的结果:
echo "$i -" $(mysqldump -B $i| bzip2 -zc | gof3r put -b s3bucket -k $i.sql.bz2 2>&1) &
- 然而,在子shell中运行的命令没有将退出状态返回到父shell,因为它并行运行,因此我无法验证它是成功还是失败。
答案 0 :(得分:6)
我认为这个命令会做你想要的:
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 ) &
在尝试与echo
一起打印之前,mysqldump ..
命令将等待$i
结果完成。子shell ( … )
和错误重定向2>&1
确保错误消息也进入回显输出。 $(
之后的空格是必要的,因为没有空格的$((
是一个不同的特殊操作 - 算术扩展。
答案 1 :(得分:5)
感谢您的所有帮助,但我想我终于找到了最佳解决方案。
基本上我使用xargs
来格式化输出,因此每个条目(转储名称+持续时间)都在一行上。我还将作业规范添加到wait
命令以获取退出状态:
等等[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
答案 2 :(得分:3)
扩展您的答案,在失败时立即退出脚本,您必须将后台进程的pid保存在数组中。在while循环中,在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
替换pids数组,但是在调用循环之前你不会得到完成的作业的pids。
上面的wait_jobs()
函数不能在子shell中使用,exit 1
调用只会终止子shell。
完整的脚本:
#!/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
答案 3 :(得分:1)
关于退出状态的其他问题,让我再写一个答案。因为$()
将运行子shell,我不认为可以像普通命令一样将退出状态返回到主shell。但是可以将退出状态写入稍后要检查的文件。请尝试以下命令。它将创建包含两行的名为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
文件。
答案 4 :(得分:0)
我会创建单独的函数来控制所有进程,然后在后台运行此函数,而不是运行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
做的事情
答案 5 :(得分:0)
您尝试使用脚本进行并行化。我建议不要重新发明轮子,而是使用久经考验的工具:GNU parallel。该教程非常庞大:http://www.gnu.org/software/parallel/parallel_tutorial.html
对于以退出值返回的作业,它有不同的选项!= 0:第一个错误中止或继续工作直到结束。
与OP脚本并行的GNU的一个优点是,它可以在第一个作业完成后立即启动第三个作业。
答案 6 :(得分:-3)
未经测试等。
#!/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 - %s\n" "$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